on f6c00f6 help sign in
50 files changed+2102−7464
since splitsnapshots
unifiedsplit
.ghci+2 .gitignore−7 .stylish-haskell.yaml−154 .travis.yml+22−13 Quickref.md+36−48 bench-cbits/checkCapability.c−209 bench/Main.hs+34−31 bench/RunAll.hs+42−113 default.nix+9−26 reflex.cabal+42−175 src/Data/Functor/Misc.hs+41−202 src/Data/WeakBag.hs−124 src/Reflex.hs+9−23 src/Reflex/Class.hs+189−836 src/Reflex/Dynamic.hs+324−388 src/Reflex/Dynamic/TH.hs+16−63 src/Reflex/Dynamic/Uniq.hs−105 src/Reflex/DynamicWriter.hs−193 src/Reflex/EventWriter.hs−170 src/Reflex/FunctorMaybe.hs−29 src/Reflex/Host/Class.hs+55−127 src/Reflex/Optimizer.hs−51 src/Reflex/Patch.hs−387 src/Reflex/PerformEvent/Base.hs−157 src/Reflex/PerformEvent/Class.hs−64 src/Reflex/PostBuild/Base.hs−142 src/Reflex/PostBuild/Class.hs−34 src/Reflex/Pure.hs−189 src/Reflex/Requester/Base.hs−151 src/Reflex/Requester/Class.hs−87 src/Reflex/Spider.hs+3−20 src/Reflex/Spider/Internal.hs+1095−1838 src/Reflex/Spider/SpiderTimeline.include.hs−122 src/Reflex/Spider/SpiderTimelineHost.include.hs−57 src/Reflex/TriggerEvent/Base.hs−111 src/Reflex/TriggerEvent/Class.hs−38 test/EventWriterT.hs−61 test/GC.hs−95 test/Reflex/Bench/Focused.hs+54−150 test/Reflex/Plan/Pure.hs−70 test/Reflex/Plan/Reflex.hs+16−30 test/Reflex/Pure.hs+76 test/Reflex/Test.hs−66 test/Reflex/Test/CrossImpl.hs+29−45 test/Reflex/Test/Micro.hs−285 test/Reflex/TestPlan.hs+8−15 test/RequesterT.hs−70 test/hlint.hs−19 test/rootCleanup.hs−25 test/semantics.hs−49
.ghci+2
@@ -1,2 +1,4 @@
11:set -isrc
2+:set -hide-package MonadCatchIO-mtl
3+:set -hide-package monads-fd
24:seti -XOverloadedStrings
.gitignore−7
@@ -1,7 +1,4 @@
11dist
2−cabal.sandbox.config
3−.cabal-sandbox/
4−dist-*
52cabal-dev
63*.o
74*.hi
@@ -32,7 +29,3 @@ tags
3229hsenv.log
3330\#*#
3431.#*
35−/shell.nix
36−/ghci-tmp
37−*.dump-*
38−*.verbose-core2core
.stylish-haskell.yamldeleted−154
@@ -1,154 +0,0 @@
1−# stylish-haskell configuration file
2−# ==================================
3−
4−# The stylish-haskell tool is mainly configured by specifying steps. These steps
5−# are a list, so they have an order, and one specific step may appear more than
6−# once (if needed). Each file is processed by these steps in the given order.
7−steps:
8− # Convert some ASCII sequences to their Unicode equivalents. This is disabled
9− # by default.
10− # - unicode_syntax:
11− # # In order to make this work, we also need to insert the UnicodeSyntax
12− # # language pragma. If this flag is set to true, we insert it when it's
13− # # not already present. You may want to disable it if you configure
14− # # language extensions using some other method than pragmas. Default:
15− # # true.
16− # add_language_pragma: true
17−
18− # Import cleanup
19− - imports:
20− # There are different ways we can align names and lists.
21− #
22− # - global: Align the import names and import list throughout the entire
23− # file.
24− #
25− # - file: Like global, but don't add padding when there are no qualified
26− # imports in the file.
27− #
28− # - group: Only align the imports per group (a group is formed by adjacent
29− # import lines).
30− #
31− # - none: Do not perform any alignment.
32− #
33− # Default: global.
34− align: none
35−
36− # Folowing options affect only import list alignment.
37− #
38− # List align has following options:
39− #
40− # - after_alias: Import list is aligned with end of import including
41− # 'as' and 'hiding' keywords.
42− #
43− # > import qualified Data.List as List (concat, foldl, foldr, head,
44− # > init, last, length)
45− #
46− # - with_alias: Import list is aligned with start of alias or hiding.
47− #
48− # > import qualified Data.List as List (concat, foldl, foldr, head,
49− # > init, last, length)
50− #
51− # - new_line: Import list starts always on new line.
52− #
53− # > import qualified Data.List as List
54− # > (concat, foldl, foldr, head, init, last, length)
55− #
56− # Default: after alias
57− list_align: after_alias
58−
59− # Long list align style takes effect when import is too long. This is
60− # determined by 'columns' setting.
61− #
62− # - inline: This option will put as much specs on same line as possible.
63− #
64− # - new_line: Import list will start on new line.
65− #
66− # - new_line_multiline: Import list will start on new line when it's
67− # short enough to fit to single line. Otherwise it'll be multiline.
68− #
69− # - multiline: One line per import list entry.
70− # Type with contructor list acts like single import.
71− #
72− # > import qualified Data.Map as M
73− # > ( empty
74− # > , singleton
75− # > , ...
76− # > , delete
77− # > )
78− #
79− # Default: inline
80− long_list_align: inline
81−
82− # List padding determines indentation of import list on lines after import.
83− # This option affects 'list_align' and 'long_list_align'.
84− list_padding: 2
85−
86− # Separate lists option affects formating of import list for type
87− # or class. The only difference is single space between type and list
88− # of constructors, selectors and class functions.
89− #
90− # - true: There is single space between Foldable type and list of it's
91− # functions.
92− #
93− # > import Data.Foldable (Foldable (fold, foldl, foldMap))
94− #
95− # - false: There is no space between Foldable type and list of it's
96− # functions.
97− #
98− # > import Data.Foldable (Foldable(fold, foldl, foldMap))
99− #
100− # Default: true
101− separate_lists: true
102−
103− # Language pragmas
104− - language_pragmas:
105− # We can generate different styles of language pragma lists.
106− #
107− # - vertical: Vertical-spaced language pragmas, one per line.
108− #
109− # - compact: A more compact style.
110− #
111− # - compact_line: Similar to compact, but wrap each line with
112− # `{-#LANGUAGE #-}'.
113− #
114− # Default: vertical.
115− style: vertical
116−
117− # Align affects alignment of closing pragma brackets.
118− #
119− # - true: Brackets are aligned in same collumn.
120− #
121− # - false: Brackets are not aligned together. There is only one space
122− # between actual import and closing bracket.
123− #
124− # Default: true
125− align: false
126−
127− # stylish-haskell can detect redundancy of some language pragmas. If this
128− # is set to true, it will remove those redundant pragmas. Default: true.
129− remove_redundant: true
130−
131− # Align the types in record declarations
132− # - records: {}
133−
134− # Replace tabs by spaces. This is disabled by default.
135− # - tabs:
136− # # Number of spaces to use for each tab. Default: 8, as specified by the
137− # # Haskell report.
138− # spaces: 8
139−
140− # Remove trailing whitespace
141− - trailing_whitespace: {}
142−
143−# A common setting is the number of columns (parts of) code will be wrapped
144−# to. Different steps take this into account. Default: 80.
145−columns: 110
146−
147−# Sometimes, language extensions are specified in a cabal file or from the
148−# command line instead of using language pragmas in the file. stylish-haskell
149−# needs to be aware of these, so it can parse the file correctly.
150−#
151−# No language extensions are enabled by default.
152−# language_extensions:
153− # - TemplateHaskell
154− # - QuasiQuotes
.travis.yml+22−13
@@ -1,15 +1,24 @@
1−# Cache Buster: 0
2−language: nix
3−cache:
4− directories:
5− - /nix
6−matrix:
7− include:
8− - env: COMPILER=ghc7103
9− - env: COMPILER=ghc801
10− - env: COMPILER=ghcjs
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
117script:
12− - set -e
13− - nix-build -E "with (import <nixpkgs> {}); pkgs.haskell.packages.$COMPILER.callPackage ./default.nix { }"
14− - nix-collect-garbage -d # This is important to do right before 'script' ends so the cache gets GCed
8+ - cabal-$CABALVER configure -v2 --enable-tests
9+ - cabal-$CABALVER build
10+ - cabal-$CABALVER test
11+ - cabal-$CABALVER sdist # tests that a source-distribution can be generated
12+# Check that the resulting source distribution can be built & installed.
13+# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
14+# `cabal install --force-reinstalls dist/*-*.tar.gz`
15+ - SRC_TGZ=$(cabal-$CABALVER info . | awk '{print $2;exit}').tar.gz &&
16+ (cd dist && cabal-$CABALVER install --force-reinstalls "$SRC_TGZ")
1517
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+ fast_finish: true
Quickref.md+36−48
@@ -37,33 +37,28 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
3737[ ] traceEventWith :: (a -> String) -> Event a -> Event a
3838
3939-- 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
40+[ ] gate :: Behavior Bool -> Event a -> Event a
41+[ ] tag :: Behavior a -> Event b -> Event a
42+[ ] tagDyn :: Dynamic a -> Event b -> Event a
43+[ ] attach :: Behavior a -> Event b -> Event (a, b)
44+[ ] attachDyn :: Dynamic a -> Event b -> Event (a, b)
45+[ ] attachWith :: (a -> b -> c) -> Behavior a -> Event b -> Event c
46+[ ] attachDynWith :: (a -> b -> c) -> Dynamic a -> Event b -> Event c
47+[ ] attachWithMaybe :: (a -> b -> Maybe c) -> Behavior a -> Event b -> Event c
48+[ ] attachDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
4949
5050-- 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)
51+[ ] <> :: Monoid a => Event a -> Event a -> Event a
52+[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
53+[ ] leftmost :: [Event a] -> Event a
54+[ ] mergeList :: [Event a] -> Event (NonEmpty a)
55+[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
56+[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
6057
6158-- 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)
59+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
60+[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
61+[ ] select :: EventSelector k -> k a -> Event a
6762
6863-- Event to Event via function that can sample current values
6964[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
@@ -89,11 +84,11 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
8984[H] hold :: a -> Event a -> m (Behavior a)
9085
9186-- 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
87+[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
88+[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
89+[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
90+[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
91+[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
9792-- ... plus many more due to typeclass membership
9893
9994-- Behavior to Behavior by sampling current values
@@ -122,23 +117,23 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
122117[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
123118
124119-- 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)
120+[H] forDyn :: Dynamic a -> (a -> b) -> m (Dynamic b)
121+[H] mapDyn :: (a -> b) -> Dynamic a -> m (Dynamic b)
122+[H] mapDynM :: (a -> m' b) -> Dynamic a -> m (Dynamic b)
123+ -- Note m' supplies [S] context
124+[H] splitDyn :: Dynamic (a, b) -> m (Dynamic a, Dynamic b)
128125
129126-- 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
127+[H] mconcatDyn :: Monoid a => [Dynamic a] -> m (Dynamic a)
128+[H] distributeDMapOverDyn :: GCompare k => DMap (WrapArg Dynamic k) -> m (Dynamic (DMap k))
129+[H] combineDyn :: (a -> b -> c) -> Dynamic a -> Dynamic b -> m (Dynamic c)
135130
136131-- Efficient one-to-many fanout
137−[ ] demux :: Ord k => Dynamic k -> Demux k
138−[ ] demuxed :: Eq k => Demux k -> k -> Dynamic Bool
132+[ ] demux :: Ord k => Dynamic k -> Demux k
133+[H] getDemuxed :: Eq k => Demux k -> k -> m (Dynamic Bool)
139134
140135-- Dynamic to Dynamic, removing updates w/o value change
141−[ ] uniqDyn :: Eq a => Dynamic a -> Dynamic a
136+[ ] nubDyn :: Eq a => Dynamic a -> Dynamic a
142137
143138-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
144139[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
@@ -161,16 +156,9 @@ For Events, the returned Event fires whenever the latest Event supplied by the w
161156-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
162157[ ] coincidence :: Event (Event a) -> Event a
163158
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−
171159-- 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
160+-- Output updated whenever inner OR outer Dynamic updates.
161+[ ] joinDyn :: Dynamic (Dynamic a) -> Dynamic a
174162[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
175163
176164-- Analogous to 'hold': Create a Behavior that is initially identical to the
bench-cbits/checkCapability.cdeleted−209
@@ -1,209 +0,0 @@
1−#include <Rts.h>
2−
3−// The InCall structure represents either a single in-call from C to
4−// Haskell, or a worker thread.
5−typedef struct InCall_ {
6− StgTSO * tso; // the bound TSO (or NULL for a worker)
7−
8− StgTSO * suspended_tso; // the TSO is stashed here when we
9− // make a foreign call (NULL otherwise);
10−
11− Capability *suspended_cap; // The capability that the
12− // suspended_tso is on, because
13− // we can't read this from the TSO
14− // without owning a Capability in the
15− // first place.
16−
17− SchedulerStatus rstat; // return status
18− StgClosure ** ret; // return value
19−
20− struct Task_ *task;
21−
22− // When a Haskell thread makes a foreign call that re-enters
23− // Haskell, we end up with another Task associated with the
24− // current thread. We have to remember the whole stack of InCalls
25− // associated with the current Task so that we can correctly
26− // save & restore the InCall on entry to and exit from Haskell.
27− struct InCall_ *prev_stack;
28−
29− // Links InCalls onto suspended_ccalls, spare_incalls
30− struct InCall_ *prev;
31− struct InCall_ *next;
32−} InCall;
33−
34−typedef struct Task_ {
35−#if defined(THREADED_RTS)
36− OSThreadId id; // The OS Thread ID of this task
37−
38− Condition cond; // used for sleeping & waking up this task
39− Mutex lock; // lock for the condition variable
40−
41− // this flag tells the task whether it should wait on task->cond
42− // or just continue immediately. It's a workaround for the fact
43− // that signalling a condition variable doesn't do anything if the
44− // thread is already running, but we want it to be sticky.
45− rtsBool wakeup;
46−#endif
47−
48− // This points to the Capability that the Task "belongs" to. If
49− // the Task owns a Capability, then task->cap points to it. If
50− // the task does not own a Capability, then either (a) if the task
51− // is a worker, then task->cap points to the Capability it belongs
52− // to, or (b) it is returning from a foreign call, then task->cap
53− // points to the Capability with the returning_worker queue that this
54− // this Task is on.
55− //
56− // When a task goes to sleep, it may be migrated to a different
57− // Capability. Hence, we always check task->cap on wakeup. To
58− // syncrhonise between the migrater and the migratee, task->lock
59− // must be held when modifying task->cap.
60− struct Capability_ *cap;
61−
62− // The current top-of-stack InCall
63− struct InCall_ *incall;
64−
65− nat n_spare_incalls;
66− struct InCall_ *spare_incalls;
67−
68− rtsBool worker; // == rtsTrue if this is a worker Task
69− rtsBool stopped; // this task has stopped or exited Haskell
70−
71− // So that we can detect when a finalizer illegally calls back into Haskell
72− rtsBool running_finalizers;
73−
74− // Links tasks on the returning_tasks queue of a Capability, and
75− // on spare_workers.
76− struct Task_ *next;
77−
78− // Links tasks on the all_tasks list; need ACQUIRE_LOCK(&all_tasks_mutex)
79− struct Task_ *all_next;
80− struct Task_ *all_prev;
81−
82−} Task;
83−
84−struct Capability_ {
85− // State required by the STG virtual machine when running Haskell
86− // code. During STG execution, the BaseReg register always points
87− // to the StgRegTable of the current Capability (&cap->r).
88− StgFunTable f;
89− StgRegTable r;
90−
91− nat no; // capability number.
92−
93− // The Task currently holding this Capability. This task has
94− // exclusive access to the contents of this Capability (apart from
95− // returning_tasks_hd/returning_tasks_tl).
96− // Locks required: cap->lock.
97− Task *running_task;
98−
99− // true if this Capability is running Haskell code, used for
100− // catching unsafe call-ins.
101− rtsBool in_haskell;
102−
103− // Has there been any activity on this Capability since the last GC?
104− nat idle;
105−
106− rtsBool disabled;
107−
108− // The run queue. The Task owning this Capability has exclusive
109− // access to its run queue, so can wake up threads without
110− // taking a lock, and the common path through the scheduler is
111− // also lock-free.
112− StgTSO *run_queue_hd;
113− StgTSO *run_queue_tl;
114−
115− // Tasks currently making safe foreign calls. Doubly-linked.
116− // When returning, a task first acquires the Capability before
117− // removing itself from this list, so that the GC can find all
118− // the suspended TSOs easily. Hence, when migrating a Task from
119− // the returning_tasks list, we must also migrate its entry from
120− // this list.
121− InCall *suspended_ccalls;
122−
123− // One mutable list per generation, so we don't need to take any
124− // locks when updating an old-generation thunk. This also lets us
125− // keep track of which closures this CPU has been mutating, so we
126− // can traverse them using the right thread during GC and avoid
127− // unnecessarily moving the data from one cache to another.
128− bdescr **mut_lists;
129− bdescr **saved_mut_lists; // tmp use during GC
130−
131− // block for allocating pinned objects into
132− bdescr *pinned_object_block;
133− // full pinned object blocks allocated since the last GC
134− bdescr *pinned_object_blocks;
135−
136− // per-capability weak pointer list associated with nursery (older
137− // lists stored in generation object)
138− StgWeak *weak_ptr_list_hd;
139− StgWeak *weak_ptr_list_tl;
140−
141− // Context switch flag. When non-zero, this means: stop running
142− // Haskell code, and switch threads.
143− int context_switch;
144−
145− // Interrupt flag. Like the context_switch flag, this also
146− // indicates that we should stop running Haskell code, but we do
147− // *not* switch threads. This is used to stop a Capability in
148− // order to do GC, for example.
149− //
150− // The interrupt flag is always reset before we start running
151− // Haskell code, unlike the context_switch flag which is only
152− // reset after we have executed the context switch.
153− int interrupt;
154−
155− // Total words allocated by this cap since rts start
156− // See [Note allocation accounting] in Storage.c
157− W_ total_allocated;
158−
159−#if defined(THREADED_RTS)
160− // Worker Tasks waiting in the wings. Singly-linked.
161− Task *spare_workers;
162− nat n_spare_workers; // count of above
163−
164− // This lock protects:
165− // running_task
166− // returning_tasks_{hd,tl}
167− // wakeup_queue
168− // inbox
169− Mutex lock;
170−
171− // Tasks waiting to return from a foreign call, or waiting to make
172− // a new call-in using this Capability (NULL if empty).
173− // NB. this field needs to be modified by tasks other than the
174− // running_task, so it requires cap->lock to modify. A task can
175− // check whether it is NULL without taking the lock, however.
176− Task *returning_tasks_hd; // Singly-linked, with head/tail
177− Task *returning_tasks_tl;
178−
179− // Messages, or END_TSO_QUEUE.
180− // Locks required: cap->lock
181− Message *inbox;
182−
183− SparkPool *sparks;
184−
185− // Stats on spark creation/conversion
186− SparkCounters spark_stats;
187−#if !defined(mingw32_HOST_OS)
188− // IO manager for this cap
189− int io_manager_control_wr_fd;
190−#endif
191−#endif
192−
193− // Per-capability STM-related data
194− StgTVarWatchQueue *free_tvar_watch_queues;
195− StgInvariantCheckQueue *free_invariant_check_queues;
196− StgTRecChunk *free_trec_chunks;
197− StgTRecHeader *free_trec_headers;
198− nat transaction_tokens;
199−} // typedef Capability is defined in RtsAPI.h
200− // We never want a Capability to overlap a cache line with anything
201− // else, so round it up to a cache line size:
202−#ifndef mingw32_HOST_OS
203− ATTRIBUTE_ALIGNED(64)
204−#endif
205− ;
206−
207−HsBool myCapabilityHasOtherRunnableThreads() {
208− return rts_unsafeGetMyCapability()->run_queue_hd == END_TSO_QUEUE ? HS_BOOL_FALSE : HS_BOOL_TRUE;
209−}
bench/Main.hs+34−31
@@ -1,27 +1,30 @@
1−{-# LANGUAGE BangPatterns #-}
21{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE BangPatterns #-}
43{-# LANGUAGE TupleSections #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
55{-# LANGUAGE TypeFamilies #-}
6−
7−-- The instance for NFData (TVar a) is an orphan, but necessary here
8−{-# OPTIONS_GHC -fno-warn-orphans #-}
9−
106module Main where
117
12−import Control.Concurrent.STM
13−import Control.DeepSeq
14−import Control.Exception (evaluate)
15−import Control.Monad.Identity
8+import Data.Functor.Misc
9+import Control.Monad.Primitive
1610import Control.Monad.IO.Class
17−import Criterion.Main
18−import Data.Dependent.Map (DMap)
19−import qualified Data.Dependent.Map as DMap
11+import Control.Monad.Identity
2012import Data.Dependent.Sum
21−import Data.Functor.Misc
13+import Control.Concurrent.STM
14+import Control.Applicative
15+import System.IO.Unsafe
2216import Data.IORef
17+import Control.DeepSeq
18+import Control.Exception (evaluate)
19+import Control.Monad
2320import Reflex
2421import Reflex.Host.Class
22+import System.Mem
23+import System.IO
24+import Criterion.Main
25+
26+import qualified Data.Dependent.Map as DM
27+
2528
2629main :: IO ()
2730main = defaultMain
@@ -39,14 +42,15 @@ newtype WHNF a = WHNF a
3942instance NFData (WHNF a) where
4043 rnf (WHNF a) = seq a ()
4144
42−withSetup :: NFData b => String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
45+withSetup :: NFData b => String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
4346withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
4447 bench name . nfIO $ runSpiderHost (action a)
4548
46−withSetupWHNF :: String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
49+withSetupWHNF :: String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
4750withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
4851 bench name . whnfIO $ runSpiderHost (action a)
4952
53+
5054micros :: [Benchmark]
5155micros =
5256 [ bench "newIORef" $ whnfIO $ void $ newIORef ()
@@ -69,41 +73,40 @@ micros =
6973 (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
7074 , withSetupWHNF "fireEventsOnly"
7175 (newEventWithTriggerRef >>= subscribePair)
72− (\(_, trigger) -> do
76+ (\(subd, trigger) -> do
7377 Just key <- liftIO $ readIORef trigger
7478 fireEvents [key :=> Identity (42 :: Int)])
7579 , withSetupWHNF "fireEventsAndRead(head/merge1)"
7680 (setupMerge 1 >>= subscribePair)
77− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
81+ (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
7882 , withSetupWHNF "fireEventsAndRead(head/merge100)"
7983 (setupMerge 100 >>= subscribePair)
80− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
84+ (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
8185 , withSetupWHNF "fireEventsAndRead(head/merge10000)"
8286 (setupMerge 10000 >>= subscribePair)
83− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
87+ (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
8488 , withSetupWHNF "fireEventsOnly(head/merge100)"
8589 (setupMerge 100 >>= subscribePair)
86− (\(_, t:_) -> do
90+ (\(subd, t:riggers) -> do
8791 Just key <- liftIO $ readIORef t
8892 fireEvents [key :=> Identity (42 :: Int)])
89− , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev, _) -> hold (42 :: Int) ev
90− , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
93+ , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev,trigger) -> hold (42 :: Int) ev
94+ , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
9195 ]
9296
9397setupMerge :: Int
94− -> SpiderHost Global ( Event (SpiderEnv Global) (DMap (Const2 Int a) Identity)
95− , [IORef (Maybe (EventTrigger Spider a))]
96− )
98+ -> SpiderHost (Event Spider (DM.DMap (Const2 Int a) Identity),
99+ [IORef (Maybe (EventTrigger Spider a))])
97100setupMerge num = do
98− (evs, triggers) <- unzip <$> replicateM num newEventWithTriggerRef
99− let !m = DMap.fromList [Const2 i :=> v | (i,v) <- zip [0..] evs]
101+ (evs, triggers) <- unzip <$> replicateM 100 newEventWithTriggerRef
102+ let !m = DM.fromList [(Const2 i) :=> v | (i,v) <- zip [0..] evs]
100103 pure (merge m, triggers)
101104
102−subscribePair :: (Event (SpiderEnv Global) a, b) -> SpiderHost Global (EventHandle (SpiderEnv Global) a, b)
105+subscribePair :: (Event Spider a, b) -> SpiderHost (EventHandle Spider a, b)
103106subscribePair (ev, b) = (,b) <$> subscribeEvent ev
104107
105−fireAndRead :: IORef (Maybe (EventTrigger (SpiderEnv Global) a)) -> a -> EventHandle (SpiderEnv Global) b
106− -> SpiderHost Global (Maybe b)
108+fireAndRead :: IORef (Maybe (EventTrigger Spider a)) -> a -> EventHandle Spider b
109+ -> SpiderHost (Maybe b)
107110fireAndRead trigger val subd = do
108111 Just key <- liftIO $ readIORef trigger
109112 fireEventsAndRead [key :=> Identity val] $ readEvent subd >>= sequence
bench/RunAll.hs+42−113
@@ -1,18 +1,4 @@
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 #-}
1+{-# LANGUAGE ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
162{-# OPTIONS_GHC -fno-warn-orphans #-}
173
184module Main where
@@ -23,135 +9,78 @@ import Criterion.Types
239import Reflex
2410import Reflex.Host.Class
2511
26−import Reflex.Plan.Reflex
2712import Reflex.TestPlan
13+import Reflex.Plan.Reflex
2814
29−import qualified Reflex.Bench.Focused as Focused
3015import Reflex.Spider.Internal (SpiderEventHandle)
16+import qualified Reflex.Bench.Focused as Focused
3117
3218import Control.Applicative
3319import Control.DeepSeq (NFData (..))
3420
35−import Prelude
3621import System.IO
3722import 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
23+import Prelude
6224
6325type MonadReflexHost' t m = (MonadReflexHost t m, MonadIORef m, MonadIORef (HostFrame t))
6426
6527
66−setupFiring :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (EventHandle t a, Schedule t)
28+setupFiring :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (Ignore (EventHandle t a), Schedule t)
6729setupFiring p = do
6830 (e, s) <- runPlan p
6931 h <- subscribeEvent e
70− return (h, s)
32+ return (Ignore h, s)
7133
7234-- Hack to avoid the NFData constraint for EventHandle which is a synonym
7335newtype Ignore a = Ignore a
7436instance NFData (Ignore a) where
7537 rnf !_ = ()
7638
77−instance NFData (SpiderEventHandle x a) where
39+instance NFData (SpiderEventHandle a) where
7840 rnf !_ = ()
7941
8042instance NFData (Behavior t a) where
8143 rnf !_ = ()
8244
8345instance NFData (Firing t) where
84− rnf !_ = ()
46+ rnf !(Firing _ _) = ()
8547
8648-- 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
49+benchFiring :: (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> (String, TestCase) -> Benchmark
50+benchFiring runHost (name, TestE p) = env setup (\e -> bench name $ whnfIO $ run e) where
51+ run (Ignore h, s) = runHost (readSchedule s (readEvent' h)) >> performGC
52+ setup = runHost $ setupFiring p
53+
54+benchFiring runHost (name, TestB p) = env setup (\e -> bench name $ whnfIO $ run e) where
55+ run (b, s) = runHost (readSchedule s (sample b)) >> performGC
56+ setup = runHost $ do
9857 (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
58+ return (b, makeDense s)
14459
14560main :: IO ()
14661main = 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
62+ hSetBuffering stdout LineBuffering
63+ defaultMainWith (defaultConfig { timeLimit = 10, csvFile = Just "dmap-original.csv" })
64+ [ benchImpl "spider" runSpiderHost
65+ ]
66+
67+benchImpl :: (MonadReflexHost' t m, MonadSample t m) => String -> (forall a. m a -> IO a) -> Benchmark
68+benchImpl name runHost = bgroup name [ sub 100 40
69+ , dynamics 100
70+ , dynamics 1000
71+ , firing 1000
72+ , firing 10000
73+ , merging 10
74+ , merging 50
75+ , merging 100
76+ , merging 200]
77+ where
78+ sub n frames = runGroup ("subscribing " ++ show (n, frames)) $ Focused.subscribing n frames
79+ firing n = runGroup ("firing " ++ show n) $ Focused.firing n
80+ merging n = runGroup ("merging " ++ show n) $ Focused.merging n
81+ dynamics n = runGroup ("dynamics " ++ show n) $ Focused.dynamics n
82+
83+ runGroup name' benchmarks = bgroup name' (benchFiring runHost <$> benchmarks)
84+
85+
86+
default.nix+9−26
@@ -1,34 +1,17 @@
1−{ mkDerivation, ghc, base, bifunctors, containers, deepseq
2−, dependent-map, dependent-sum, exception-transformers
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
4−, monad-control, mtl, primitive, ref-tf, semigroupoids
5−, semigroups, split, stdenv, stm, syb, template-haskell
6−, these, transformers, transformers-compat, prim-uniq
7−, data-default
8−, useTemplateHaskell ? true
1+{ mkDerivation, dependent-map, dependent-sum
2+, mtl, ref-tf, semigroups, these, MemoTrie, exception-transformers
3+, haskell-src-exts, haskell-src-meta
94}:
105mkDerivation {
116 pname = "reflex";
12− version = "0.5.0";
7+ version = "0.4.0";
138 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
14− libraryHaskellDepends = [
15− base bifunctors containers dependent-map dependent-sum
16− exception-transformers lens
17− MemoTrie monad-control mtl primitive ref-tf semigroupoids
18− semigroups stm syb template-haskell these transformers
19− transformers-compat prim-uniq
20− base bifunctors containers deepseq dependent-map dependent-sum
21− mtl ref-tf split transformers data-default
22− ] ++ (if !useTemplateHaskell then [] else [
9+ buildDepends = [
10+ dependent-map dependent-sum mtl ref-tf semigroups these exception-transformers
2311 haskell-src-exts haskell-src-meta
24− ]);
25− testHaskellDepends = if ghc.isGhcjs or false then [] else [
26− hlint
2712 ];
28− configureFlags = if useTemplateHaskell then [] else [
29− "-f-use-template-haskell"
13+ testDepends = [
14+ MemoTrie
3015 ];
31− homepage = "https://github.com/reflex-frp/reflex";
32− description = "Higher-order Functional Reactive Programming";
33− license = stdenv.lib.licenses.bsd3;
16+ license = null;
3417}
reflex.cabal+42−175
@@ -1,5 +1,5 @@
11Name: reflex
2−Version: 0.5.0
2+Version: 0.4.0.1
33Synopsis: Higher-order Functional Reactive Programming
44Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
55License: BSD3
@@ -13,224 +13,91 @@ Cabal-version: >=1.9.2
1313homepage: https://github.com/reflex-frp/reflex
1414bug-reports: https://github.com/reflex-frp/reflex/issues
1515
16−flag specialize-to-spidertimeline-global
17− description: Specialize all Reflex functions to the SpiderTimeline Global implementation. This may improve performance, but will disable all other implementations.
18− default: False
19− manual: True
20−
21−flag use-reflex-optimizer
22− description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
23− default: False
24− manual: True
25−
26−flag use-template-haskell
27− description: Use template haskell to generate lenses
28− default: True
29− manual: True
30−
31−flag expose-all-unfoldings
32− description: Build the library with -fexpose-all-unfoldings which can help client code specialize better
33− default: False
34− manual: True
35−
36−flag debug-trace-events
37− description: add instrumentation that outputs the stack trace of the definition of an event whenever it is subscribed to. Warning: It is very slow!
38− default: False
39− manual: True
40−
4116library
4217 hs-source-dirs: src
4318 build-depends:
44− MemoTrie == 0.6.*,
45− base >= 4.7 && < 4.11,
46− bifunctors >= 5.2 && < 5.5,
47− containers == 0.5.*,
48− data-default >= 0.5 && < 0.8,
49− dependent-map >= 0.2.4 && < 0.3,
50− exception-transformers == 0.4.*,
51− lens >= 4.7 && < 5,
52− monad-control >= 1.0.1 && < 1.1,
19+ base >= 4.7 && < 4.10,
20+ dependent-sum >= 0.3 && < 0.5,
21+ dependent-map == 0.2.*,
22+ semigroups >= 0.16 && < 0.19,
5323 mtl >= 2.1 && < 2.3,
54− prim-uniq >= 0.1.0.1 && < 0.2,
24+ containers == 0.5.*,
25+ these >= 0.4 && < 0.8,
5526 primitive >= 0.5 && < 0.7,
27+ template-haskell >= 2.9 && < 2.13,
5628 ref-tf == 0.4.*,
57− reflection == 2.1.*,
58− semigroupoids >= 4.0 && < 6,
59− semigroups >= 0.16 && < 0.19,
60− stm == 2.4.*,
61− syb >= 0.5 && < 0.7,
62− these >= 0.4 && < 0.8,
29+ exception-transformers == 0.4.*,
6330 transformers >= 0.2,
64− transformers-compat >= 0.3
31+ transformers-compat >= 0.3,
32+ haskell-src-exts >= 1.16 && < 1.20,
33+ haskell-src-meta >= 0.6 && < 0.9,
34+ syb >= 0.4.4 && < 0.8
6535
6636 exposed-modules:
67− Data.Functor.Misc,
68− Data.WeakBag,
6937 Reflex,
38+ Reflex.Spider,
39+ Reflex.Spider.Internal,
7040 Reflex.Class,
71− Reflex.EventWriter,
7241 Reflex.Dynamic,
73− Reflex.Dynamic.Uniq,
74− Reflex.DynamicWriter,
75− Reflex.FunctorMaybe,
42+ Reflex.Dynamic.TH,
7643 Reflex.Host.Class,
77− Reflex.Patch,
78− Reflex.TriggerEvent.Base,
79− Reflex.TriggerEvent.Class,
80− Reflex.PerformEvent.Base,
81− Reflex.PerformEvent.Class,
82− Reflex.PostBuild.Base,
83− Reflex.PostBuild.Class,
84− Reflex.Requester.Base,
85− Reflex.Requester.Class,
86− Reflex.Spider,
87− Reflex.Spider.Internal
44+ Data.Functor.Misc
8845
46+ other-extensions: TemplateHaskell
8947 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
90− ghc-prof-options: -fprof-auto
91−
92− if flag(specialize-to-spidertimeline-global)
93− cpp-options: -DSPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
94− else
95− exposed-modules: Reflex.Pure
96−
97− if flag(debug-trace-events)
98− cpp-options: -DDEBUG_TRACE_EVENTS
99− build-depends: bytestring
100−
101− if flag(use-reflex-optimizer)
102− cpp-options: -DUSE_REFLEX_OPTIMIZER
103− build-depends: ghc
104− exposed-modules: Reflex.Optimizer
10548
106− if flag(use-template-haskell)
107− cpp-options: -DUSE_TEMPLATE_HASKELL
108− build-depends:
109− dependent-sum >= 0.3 && < 0.5,
110− haskell-src-exts >= 1.16 && < 1.19,
111− haskell-src-meta >= 0.6 && < 0.8,
112− template-haskell >= 2.9 && < 2.12
113− exposed-modules:
114− Reflex.Dynamic.TH
115− other-extensions: TemplateHaskell
116− else
117− build-depends:
118− dependent-sum == 0.4.*
119−
120− if flag(expose-all-unfoldings)
121− ghc-options: -fexpose-all-unfoldings
122−
123−test-suite semantics
49+test-suite cross-impl
12450 type: exitcode-stdio-1.0
125− main-is: semantics.hs
51+ main-is: Reflex/Test/CrossImpl.hs
52+ other-modules:
53+ Reflex.Pure
54+ ghc-options: -O2 -main-is Reflex.Test.CrossImpl.test
12655 hs-source-dirs: test
127− ghc-options: -O2 -Wall -rtsopts
12856 build-depends:
12957 base,
130− bifunctors,
58+ reflex,
59+ ref-tf,
60+ mtl,
13161 containers,
132− deepseq >= 1.3 && < 1.5,
13362 dependent-map,
134− dependent-sum,
135− mtl,
136− ref-tf,
137− reflex,
138− split,
139− transformers >= 0.3
140− if flag(specialize-to-spidertimeline-global)
141− buildable: False
142−
143−test-suite hlint
144− type: exitcode-stdio-1.0
145− main-is: hlint.hs
146− hs-source-dirs: test
147− build-depends: base, hlint == 1.9.*
148− if impl(ghcjs)
149− buildable: False
150−
151−test-suite EventWriterT
152− type: exitcode-stdio-1.0
153− main-is: EventWriterT.hs
154− hs-source-dirs: test
155− build-depends: base
156− , dependent-sum
157− , transformers
158− , reflex
159− , ref-tf
160−
161−test-suite RequesterT
162− type: exitcode-stdio-1.0
163− main-is: RequesterT.hs
164− hs-source-dirs: test
165− build-depends: base
166− , dependent-sum
167− , dependent-map
168− , transformers
169− , reflex
170− , ref-tf
171−
172−test-suite GC-Semantics
173− type: exitcode-stdio-1.0
174− main-is: GC.hs
175− hs-source-dirs: test
176− if flag(specialize-to-spidertimeline-global)
177− buildable: False
178− build-depends: base
179− , dependent-sum
180− , dependent-map
181− , these
182− , transformers
183− , reflex
184− , ref-tf
185−
186−test-suite rootCleanup
187− type: exitcode-stdio-1.0
188− main-is: rootCleanup.hs
189− hs-source-dirs: test
190− build-depends: base
191− , reflex
63+ MemoTrie == 0.6.*
19264
19365benchmark spider-bench
19466 type: exitcode-stdio-1.0
19567 hs-source-dirs: bench
19668 main-is: Main.hs
197− ghc-options: -Wall -O2 -rtsopts
69+ ghc-options: -O2 -rtsopts
19870 build-depends:
19971 base,
200− criterion == 1.1.*,
201− deepseq >= 1.3 && < 1.5,
202− dependent-map,
20372 dependent-sum,
73+ dependent-map,
74+ transformers >= 0.3 && < 0.6,
75+ stm == 2.4.*,
76+ deepseq >= 1.3 && < 1.5,
20477 mtl,
20578 primitive,
206− reflex,
207− stm,
208− transformers >= 0.3
79+ criterion >= 1.1 && < 1.3,
80+ reflex
20981
21082benchmark saulzar-bench
21183 type: exitcode-stdio-1.0
21284 hs-source-dirs: bench test
213− c-sources: bench-cbits/checkCapability.c
21485 main-is: RunAll.hs
215− ghc-options: -Wall -O2 -rtsopts -threaded
216− ghc-prof-options: -fprof-auto
86+ ghc-options: -O2 -rtsopts
21787 build-depends:
21888 base,
21989 containers == 0.5.*,
220− criterion == 1.1.*,
221− deepseq >= 1.3 && < 1.5,
222− dependent-map,
90+ ref-tf == 0.4.*,
22391 dependent-sum,
224− loch-th,
92+ dependent-map,
93+ transformers >= 0.3 && < 0.6,
94+ stm == 2.4.*,
95+ deepseq >= 1.3 && < 1.5,
22596 mtl,
22697 primitive,
227− process,
228− ref-tf,
229− reflex,
98+ criterion >= 1.1 && < 1.3,
23099 split,
231− stm,
232− time,
233− transformers >= 0.3
100+ reflex
234101
235102source-repository head
236103 type: git
src/Data/Functor/Misc.hs+41−202
@@ -1,78 +1,30 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveDataTypeable #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE KindSignatures #-}
7−{-# LANGUAGE LambdaCase #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PolyKinds #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
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
17−-- which are relevant to the use of 'Functor'-based datastructures like
18−-- 'Data.Dependent.Map.DMap'.
19−module Data.Functor.Misc
20− ( -- * Const2
21− Const2 (..)
22− , dmapToMap
23− , dmapToMapWith
24− , mapToDMap
25− , weakenDMapWith
26− -- * WrapArg
27− , WrapArg (..)
28− -- * Convenience functions for DMap
29− , mapWithFunctorToDMap
30− , mapKeyValuePairsMonotonic
31− , combineDMapsWithKey
32− , EitherTag (..)
33− , dmapToThese
34− , eitherToDSum
35− , dsumToEither
36− -- * Deprecated functions
37− , sequenceDmap
38− , wrapDMap
39− , rewrapDMap
40− , unwrapDMap
41− , unwrapDMapMaybe
42− , extractFunctorDMap
43− ) where
44−
45−import Control.Applicative (Applicative, (<$>))
46−import Control.Monad.Identity
47−import Data.Dependent.Map (DMap)
48−import qualified Data.Dependent.Map as DMap
49−import Data.Dependent.Sum
1+{-# LANGUAGE KindSignatures, GADTs, DeriveDataTypeable, RankNTypes, ScopedTypeVariables, PolyKinds #-}
2+module Data.Functor.Misc where
3+
504import Data.GADT.Compare
51−import Data.GADT.Show
525import Data.Map (Map)
536import qualified Data.Map as Map
54−import Data.Some (Some)
55−import qualified Data.Some as Some
56−import Data.These
7+import Data.Dependent.Map (DMap, DSum (..))
8+import qualified Data.Dependent.Map as DMap
579import Data.Typeable hiding (Refl)
10+import Data.These
11+import Control.Monad.Identity
5812
59−--------------------------------------------------------------------------------
60−-- Const2
61−--------------------------------------------------------------------------------
62−
63−-- | 'Const2' stores a value of a given type 'k' and ensures that a particular
64−-- type 'v' is always given for the last type parameter
65−data Const2 :: * -> x -> x -> * where
66− Const2 :: k -> Const2 k v v
67− deriving (Typeable)
13+data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
14+ WrapArg :: f a -> WrapArg g f (g a)
6815
69−deriving instance Show k => Show (Const2 k v v)
16+instance GEq f => GEq (WrapArg g f) where
17+ geq (WrapArg a) (WrapArg b) = fmap (\Refl -> Refl) $ geq a b
7018
71−instance Show k => GShow (Const2 k v) where
72− gshowsPrec n x@(Const2 _) = showsPrec n x
19+instance GCompare f => GCompare (WrapArg g f) where
20+ gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
21+ GLT -> GLT
22+ GEQ -> GEQ
23+ GGT -> GGT
7324
74−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
75− showTaggedPrec (Const2 _) = showsPrec
25+data Const2 :: * -> * -> * -> * where
26+ Const2 :: k -> Const2 k v v
27+ deriving (Typeable)
7628
7729instance Eq k => GEq (Const2 k v) where
7830 geq (Const2 a) (Const2 b) =
@@ -86,63 +38,13 @@ instance Ord k => GCompare (Const2 k v) where
8638 EQ -> GEQ
8739 GT -> GGT
8840
89−-- | Convert a 'DMap' to a regular 'Map'
90−dmapToMap :: DMap (Const2 k v) Identity -> Map k v
91−dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
92−
93−dmapToMapWith :: (f v -> v') -> DMap (Const2 k v) f -> Map k v'
94−dmapToMapWith f = Map.fromDistinctAscList . map (\(Const2 k :=> v) -> (k, f v)) . DMap.toAscList
95−
96−-- | Convert a regular 'Map' to a 'DMap'
97−mapToDMap :: Map k v -> DMap (Const2 k v) Identity
98−mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
99−
100−-- | Convert a regular 'Map', where the values are already wrapped in a functor,
101−-- to a 'DMap'
102−mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
103−mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
104−
105−weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
106−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some.This k, f v)) . DMap.toAscList
107−
108−--------------------------------------------------------------------------------
109−-- WrapArg
110−--------------------------------------------------------------------------------
111−
112−-- | 'WrapArg' can be used to tag a value in one functor with a type
113−-- representing another functor. This was primarily used with dependent-map <
114−-- 0.2, in which the value type was not wrapped in a separate functor.
115−data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
116− WrapArg :: f a -> WrapArg g f (g a)
117−
118−instance GEq f => GEq (WrapArg g f) where
119− geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
120−
121−instance GCompare f => GCompare (WrapArg g f) where
122− gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
123− GLT -> GLT
124− GEQ -> GEQ
125− GGT -> GGT
126−
127−--------------------------------------------------------------------------------
128−-- Convenience functions for DMap
129−--------------------------------------------------------------------------------
130−
131−-- | Map over all key/value pairs in a 'DMap', potentially altering the key as
132−-- well as the value. The provided function MUST preserve the ordering of the
133−-- keys, or the resulting 'DMap' will be malformed.
134−mapKeyValuePairsMonotonic :: (DSum k v -> DSum k' v') -> DMap k v -> DMap k' v'
135−mapKeyValuePairsMonotonic f = DMap.fromDistinctAscList . map f . DMap.toAscList
41+{-# INLINE sequenceDmap #-}
42+sequenceDmap :: (Monad m, GCompare f) => DMap f m -> m (DMap f Identity)
43+sequenceDmap = DMap.foldrWithKey (\k mv mx -> mx >>= \x -> mv >>= \v -> return $ DMap.insert k (Identity v) x)
44+ (return DMap.empty)
13645
13746{-# INLINE combineDMapsWithKey #-}
138−-- | Union two 'DMap's of different types, yielding another type. Each key that
139−-- is present in either input map will be present in the output.
140−combineDMapsWithKey :: forall f g h i.
141− GCompare f
142− => (forall a. f a -> These (g a) (h a) -> i a)
143− -> DMap f g
144− -> DMap f h
145− -> DMap f i
47+combineDMapsWithKey :: forall f g h i. GCompare f => (forall (a :: *). f a -> These (g a) (h a) -> i a) -> DMap f g -> DMap f h -> DMap f i
14648combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
14749 where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
14850 go [] hs = map (\(hk :=> hv) -> hk :=> f hk (That hv)) hs
@@ -152,86 +54,23 @@ combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList m
15254 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
15355 GGT -> (hk :=> f hk (That hv)) : go gs hs'
15456
155−-- | Extract the values of a 'DMap' of 'EitherTag's.
156−dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
157−dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
158− (Nothing, Nothing) -> Nothing
159− (Just (Identity a), Nothing) -> Just $ This a
160− (Nothing, Just (Identity b)) -> Just $ That b
161− (Just (Identity a), Just (Identity b)) -> Just $ These a b
162−
163−-- | Tag type for 'Either' to use it as a 'DSum'.
164−data EitherTag l r a where
165− LeftTag :: EitherTag l r l
166− RightTag :: EitherTag l r r
167−
168−instance GEq (EitherTag l r) where
169− geq a b = case (a, b) of
170− (LeftTag, LeftTag) -> Just Refl
171− (RightTag, RightTag) -> Just Refl
172− _ -> Nothing
173−
174−instance GCompare (EitherTag l r) where
175− gcompare a b = case (a, b) of
176− (LeftTag, LeftTag) -> GEQ
177− (LeftTag, RightTag) -> GLT
178− (RightTag, LeftTag) -> GGT
179− (RightTag, RightTag) -> GEQ
180−
181−instance GShow (EitherTag l r) where
182− gshowsPrec _ a = case a of
183− LeftTag -> showString "LeftTag"
184− RightTag -> showString "RightTag"
185−
186−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
187− showTaggedPrec t n (Identity a) = case t of
188− LeftTag -> showsPrec n a
189− RightTag -> showsPrec n a
190−
191−-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
192−eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
193−eitherToDSum = \case
194− Left a -> (LeftTag :=> Identity a)
195− Right b -> (RightTag :=> Identity b)
196−
197−-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
198−dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
199−dsumToEither = \case
200− (LeftTag :=> Identity a) -> Left a
201− (RightTag :=> Identity b) -> Right b
202−
203−--------------------------------------------------------------------------------
204−-- Deprecated functions
205−--------------------------------------------------------------------------------
206−
207−{-# INLINE sequenceDmap #-}
208−{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ t -> fmap Identity t)' instead" #-}
209−-- | Run the actions contained in the 'DMap'
210−sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
211−sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
212−
213−{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
214−-- | Replace the 'Identity' functor for a 'DMap''s values with a different functor
21557wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
216−wrapDMap f = DMap.map $ f . runIdentity
58+wrapDMap f = DMap.fromDistinctAscList . map (\(k :=> Identity v) -> k :=> f v) . DMap.toAscList
21759
218−{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
219−-- | Replace one functor for a 'DMap''s values with a different functor
22060rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
221−rewrapDMap = DMap.map
222−
223−{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}
224−-- | Replace one functor for a 'DMap''s values with the 'Identity' functor
225−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
226−unwrapDMap f = DMap.map $ Identity . f
227−
228−{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}
229−-- | Like 'unwrapDMap', but possibly delete some values from the DMap
230−unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
231−unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
232−
233−{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
234−-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
235−-- underlying functor
236−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
237−extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
61+rewrapDMap f = DMap.fromDistinctAscList . map (\(k :=> v) -> k :=> f v) . DMap.toAscList
62+
63+unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
64+unwrapDMap f = DMap.fromDistinctAscList . map (\(k :=> v) -> k :=> Identity (f v)) . DMap.toAscList
65+
66+unwrapDMapMaybe :: (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
67+unwrapDMapMaybe f m = DMap.fromDistinctAscList [k :=> Identity w | (k :=> v) <- DMap.toAscList m, Just w <- [f v]]
68+
69+mapToDMap :: Map k v -> DMap (Const2 k v) Identity
70+mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
71+
72+mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
73+mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> (Const2 k) :=> v) . Map.toAscList
74+
75+dmapToMap :: DMap (Const2 k v) Identity -> Map k v
76+dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
src/Data/WeakBag.hsdeleted−124
@@ -1,124 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ExistentialQuantification #-}
4−#ifdef USE_REFLEX_OPTIMIZER
5−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
6−#endif
7−-- | This module defines the 'WeakBag' type, which represents a mutable
8−-- collection of items that does not cause the items to be retained in memory.
9−-- This is useful for situations where a value needs to be inspected or modified
10−-- if it is still alive, but can be ignored if it is dead.
11−module Data.WeakBag
12− ( WeakBag
13− , WeakBagTicket
14− , empty
15− , singleton
16− , insert
17− , traverse
18− , remove
19− -- * Internal functions
20− -- These will not always be available.
21− , _weakBag_children --TODO: Don't export this
22− ) where
23−
24−import Control.Exception
25−import Control.Monad hiding (forM_, mapM_)
26−import Control.Monad.IO.Class
27−import Data.Foldable (forM_, mapM_)
28−import Data.IntMap.Strict (IntMap)
29−import qualified Data.IntMap.Strict as IntMap
30−import Data.IORef
31−import System.Mem.Weak
32−
33−import Prelude hiding (mapM_, traverse)
34−
35−-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
36−-- that is, they can still be garbage-collected. As long as the @a@s remain
37−-- alive, the 'WeakBag' will continue to refer to them.
38−data WeakBag a = WeakBag
39− { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
40− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
41− }
42−
43−-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
44−-- the caller retains the ticket, the item is guranteed to stay in memory (and
45−-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
46−-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
47−data WeakBagTicket = forall a. WeakBagTicket
48− { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
49− , _weakBagTicket_item :: {-# NOUNPACK #-} !a
50− }
51−
52−-- | Insert an item into a 'WeakBag'.
53−{-# INLINE insert #-}
54−insert :: a -- ^ The item
55− -> WeakBag a -- ^ The 'WeakBag' to insert into
56− -> IORef (Weak b) -- ^ An arbitrary value to be used in the following
57− -- callback
58− -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
59− -- (whether automatically by the item being garbage
60− -- collected or manually via 'remove')
61− -> IO WeakBagTicket -- ^ Returns a 'WeakBagTicket' that ensures the item
62− -- is retained and allows the item to be removed.
63−insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
64− a' <- evaluate a
65− wbRef' <- evaluate wbRef
66− myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
67− let cleanup = do
68− wb <- readIORef wbRef'
69− mb <- deRefWeak wb
70− forM_ mb $ \b -> do
71− csWithoutMe <- atomicModifyIORef children $ \cs ->
72− let !csWithoutMe = IntMap.delete myId cs
73− in (csWithoutMe, csWithoutMe)
74− when (IntMap.null csWithoutMe) $ finalizer b
75− return ()
76− return ()
77− wa <- mkWeakPtr a' $ Just cleanup
78− atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
79− return $ WeakBagTicket
80− { _weakBagTicket_weakItem = wa
81− , _weakBagTicket_item = a'
82− }
83−
84−-- | Create an empty 'WeakBag'.
85−{-# INLINE empty #-}
86−empty :: IO (WeakBag a)
87−empty = {-# SCC "empty" #-} do
88− nextId <- newIORef 1
89− children <- newIORef IntMap.empty
90− let bag = WeakBag
91− { _weakBag_nextId = nextId
92− , _weakBag_children = children
93− }
94− return bag
95−
96−-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
97−-- 'empty', then using 'insert'.
98−{-# INLINE singleton #-}
99−singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket)
100−singleton a wbRef finalizer = {-# SCC "singleton" #-} do
101− bag <- empty
102− ticket <- insert a bag wbRef finalizer
103− return (bag, ticket)
104−
105−{-# INLINE traverse #-}
106−-- | Visit every node in the given list. If new nodes are appended during the
107−-- traversal, they will not be visited. Every live node that was in the list
108−-- when the traversal began will be visited exactly once; however, no guarantee
109−-- is made about the order of the traversal.
110−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
111−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
112− cs <- liftIO $ readIORef children
113− forM_ cs $ \c -> do
114− ma <- liftIO $ deRefWeak c
115− mapM_ f ma
116−
117−-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
118−-- on the same 'WeakBagTicket'.
119−{-# INLINE remove #-}
120−remove :: WeakBagTicket -> IO ()
121−remove (WeakBagTicket w _) = {-# SCC "remove" #-} finalize w
122−--TODO: Should 'remove' also drop the reference to the item?
123−
124−--TODO: can/should we provide a null WeakBagTicket?
src/Reflex.hs+9−23
@@ -1,24 +1,10 @@
1−{-# LANGUAGE CPP #-}
2−-- | This module exports all of the commonly-used functionality of Reflex; if
3−-- you are just getting started with Reflex, this is probably what you want.
4−module Reflex
5− ( module X
6− ) where
1+module Reflex ( module Reflex.Class
2+ , module Reflex.Dynamic
3+ , module Reflex.Dynamic.TH
4+ , module Reflex.Spider
5+ ) where
76
8−import Reflex.Class as X
9−import Reflex.EventWriter as X
10−import Reflex.Dynamic as X
11−#ifdef USE_TEMPLATE_HASKELL
12−import Reflex.Dynamic.TH as X
13−#endif
14−import Reflex.Dynamic.Uniq as X
15−import Reflex.DynamicWriter as X
16−import Reflex.PerformEvent.Base as X
17−import Reflex.PerformEvent.Class as X
18−import Reflex.PostBuild.Base as X
19−import Reflex.PostBuild.Class as X
20−import Reflex.Requester.Base as X
21−import Reflex.Requester.Class as X
22−import Reflex.Spider as X
23−import Reflex.TriggerEvent.Base as X
24−import Reflex.TriggerEvent.Class as X
7+import Reflex.Class
8+import Reflex.Dynamic
9+import Reflex.Dynamic.TH
10+import Reflex.Spider
src/Reflex/Class.hs+189−836
@@ -1,435 +1,67 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoMonomorphismRestriction #-}
9−{-# LANGUAGE RankNTypes #-}
10−{-# LANGUAGE RecursiveDo #-}
11−{-# LANGUAGE RoleAnnotations #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE TypeFamilies #-}
14−{-# LANGUAGE TypeOperators #-}
15−{-# LANGUAGE UndecidableInstances #-}
16−#ifdef USE_REFLEX_OPTIMIZER
17−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
18−#endif
19−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
20−{-# LANGUAGE EmptyDataDecls #-}
21−{-# OPTIONS_GHC -Wno-redundant-constraints #-}
22−#endif
23−
24−-- | This module contains the Reflex interface, as well as a variety of
25−-- convenience functions for working with 'Event's, 'Behavior's, and other
26−-- signals.
27−module Reflex.Class
28− ( module Reflex.Patch
29− -- * Primitives
30− , Reflex (..)
31− , coerceBehavior
32− , coerceEvent
33− , coerceDynamic
34− , MonadSample (..)
35− , MonadHold (..)
36− -- ** 'fan'-related types
37− , EventSelector (..)
38− -- ** 'Incremental'-related types
39− -- * Convenience functions
40− , constDyn
41− , pushAlways
42− -- ** Combining 'Event's
43− , leftmost
44− , mergeMap
45− , mergeMapIncremental
46− , mergeMapIncrementalWithMove
47− , mergeList
48− , mergeWith
49− , difference
50− , alignEventWithMaybe
51− -- ** Breaking up 'Event's
52− , splitE
53− , fanEither
54− , fanThese
55− , fanMap
56− , dmapToThese
57− , EitherTag (..)
58− , eitherToDSum
59− , dsumToEither
60− -- ** Switching between 'Event's
61− , switchPromptly
62− , switchPromptOnly
63− -- ** Using 'Event's to sample 'Behavior's
64− , tag
65− , attach
66− , attachWith
67− , attachWithMaybe
68− -- ** Blocking an 'Event' based on a 'Behavior'
69− , gate
70− -- ** Combining 'Dynamic's
71− , distributeDMapOverDynPure
72− , distributeListOverDyn
73− , distributeListOverDynWith
74− , zipDyn
75− , zipDynWith
76− -- ** Accumulating state
77− , Accumulator (..)
78− , mapAccum_
79− , mapAccumM_
80− , mapAccumMaybe_
81− , mapAccumMaybeM_
82− , zipListWithEvent
83− , headE
84− , tailE
85− , headTailE
86− , switcher
87− -- * Debugging functions
88− , traceEvent
89− , traceEventWith
90− -- * Unsafe functions
91− , unsafeDynamic
92− , unsafeMapIncremental
93− -- * 'FunctorMaybe'
94− , FunctorMaybe (..)
95− , fforMaybe
96− , ffilter
97− -- * Miscellaneous convenience functions
98− , ffor
99− , MonadAdjust (..)
100− , sequenceDMapWithAdjust
101− , sequenceDMapWithAdjustWithMove
102− , mapMapWithAdjustWithMove
103− -- * Deprecated functions
104− , appendEvents
105− , onceE
106− , sequenceThese
107− , fmapMaybeCheap
108− , fmapCheap
109− , fforCheap
110− , fforMaybeCheap
111− , pushAlwaysCheap
112− , tagCheap
113− , mergeWithCheap
114− , mergeWithCheap'
115−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
116− , Spider
117− , SpiderEnv
118− , SpiderTimeline
119− , Global
120− , Behavior (..)
121− , Event (..)
122− , Dynamic (..)
123− , Incremental (..)
124− , never
125− , constant
126− , push
127− , pushCheap
128− , pull
129− , merge
130− , fan
131− , switch
132− , coincidence
133− , current
134− , updated
135− , unsafeBuildDynamic
136− , unsafeBuildIncremental
137− , mergeIncremental
138− , mergeIncrementalWithMove
139− , currentIncremental
140− , updatedIncremental
141− , incrementalToDynamic
142− , behaviorCoercion
143− , eventCoercion
144− , dynamicCoercion
145−#endif
146− ) where
1+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, CPP #-}
2+module Reflex.Class where
1473
1484import Control.Applicative
149−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
150−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
151−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
152−import Control.Monad.Trans.Cont (ContT ())
153−import Control.Monad.Trans.Except (ExceptT ())
154−import Control.Monad.Trans.RWS (RWST ())
155−import Control.Monad.Trans.Writer (WriterT ())
5+import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
6+import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
7+import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
8+import Control.Monad.Trans.Writer (WriterT())
9+import Control.Monad.Trans.Except (ExceptT())
10+import Control.Monad.Trans.Cont (ContT())
11+import Control.Monad.Trans.RWS (RWST())
12+import Data.List.NonEmpty (NonEmpty (..))
13+import Data.These
15614import Data.Align
157−import Data.Bifunctor
158−import Data.Coerce
159−import Data.Default
160−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
15+import Data.GADT.Compare (GEq (..), (:~:) (..))
16+import Data.GADT.Show (GShow (..))
17+import Data.Dependent.Sum (ShowTag (..))
18+import Data.Map (Map)
19+import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
16120import qualified Data.Dependent.Map as DMap
162−import Data.Either
163−import Data.Foldable
164−import Data.Functor.Bind hiding (join)
165−import qualified Data.Functor.Bind as Bind
166−import Data.Functor.Constant
16721import Data.Functor.Misc
168−import Data.Functor.Plus
169−import Data.List.NonEmpty (NonEmpty (..))
170−import Data.Map (Map)
171−import Data.Maybe
172−import Data.Monoid hiding (Alt, (<>))
173−import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
174−import Data.String
175−import Data.These
22+import Data.Semigroup
17623import Data.Traversable
177−import Data.Type.Coercion
178−import Reflex.FunctorMaybe
179−import Reflex.Patch
180−
181−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
182−import Reflex.Spider.Internal (EventM, Global, HasSpiderTimeline, SpiderHost (..), SpiderHostFrame (..),
183− SpiderPullM, SpiderPushM (..), behaviorHoldIdentity, dynamicConst,
184− dynamicCurrent, dynamicDynIdentity, dynamicHold, dynamicHoldIdentity,
185− dynamicUpdated, newMapDyn, readBehaviorTracked, readBehaviorUntracked,
186− runFrame, runSpiderHostFrame, unsafeDyn)
187−import qualified Reflex.Spider.Internal
188−import qualified Reflex.Spider.Internal as S
189−import Unsafe.Coerce
190−#endif
19124
19225-- Note: must come last to silence warnings due to AMP on GHC < 7.10
193−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
26+import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
19427
19528import Debug.Trace (trace)
19629
197−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
198−#include "Spider/SpiderTimeline.include.hs"
199−#endif
200−
201−-- | The 'Reflex' class contains all the primitive functionality needed for
202−-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
203−-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
204−-- and the type of the timeline determines the FRP engine to be used. For most
205−-- purposes, the 'Reflex.Spider' implementation is recommended.
206−class ( MonadHold t (PushM t)
207− , MonadSample t (PullM t)
208− , MonadFix (PushM t)
209− , Functor (Dynamic t)
210− , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
211− , Monad (Dynamic t)
212−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
213− , t ~ SpiderTimeline Global
214−#endif
215− ) => Reflex t where
216− -- | A container for a value that can change over time. 'Behavior's can be
217− -- sampled at will, but it is not possible to be notified when they change
30+class (MonadHold t (PushM t), MonadSample t (PullM t), MonadFix (PushM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
31+ -- | A container for a value that can change over time. Behaviors can be sampled at will, but it is not possible to be notified when they change
21832 data Behavior t :: * -> *
219− -- | A stream of occurrences. During any given frame, an 'Event' is either
220− -- occurring or not occurring; if it is occurring, it will contain a value of
221− -- the given type (its "occurrence type")
33+ -- | A stream of occurrences. During any given frame, an Event is either occurring or not occurring; if it is occurring, it will contain a value of the given type (its "occurrence type")
22234 data Event t :: * -> *
223− -- | A container for a value that can change over time and allows
224− -- notifications on changes. Basically a combination of a 'Behavior' and an
225− -- 'Event', with a rule that the 'Behavior' will change if and only if the
226− -- 'Event' fires.
227− data Dynamic t :: * -> *
228− -- | An 'Incremental' is a more general form of a 'Dynamic'.
229− -- Instead of always fully replacing the value, only parts of it can be patched.
230− -- This is only needed for performance critical code via `mergeIncremental` to make small
231− -- changes to large values.
232− data Incremental t :: * -> *
35+ -- | An Event with no occurrences
36+ never :: Event t a
37+ -- | Create a Behavior that always has the given value
38+ constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of Behaviors doesn't have a negative performance impact
39+ -- | Create an Event from another Event; the provided function can sample Behaviors and hold Events, and use the results to produce a occurring (Just) or non-occurring (Nothing) result
40+ push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
23341 -- | A monad for doing complex push-based calculations efficiently
23442 type PushM t :: * -> *
43+ -- | Create a Behavior by reading from other Behaviors; the result will be recomputed whenever any of the read Behaviors changes
44+ pull :: PullM t a -> Behavior t a
23545 -- | A monad for doing complex pull-based calculations efficiently
23646 type PullM t :: * -> *
237−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
238− -- | An 'Event' with no occurrences
239− never :: Event t a
240− -- | Create a 'Behavior' that always has the given value
241− 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
242− -- | Create an 'Event' from another 'Event'; the provided function can sample
243− -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
244− -- (Just) or non-occurring (Nothing) result
245− push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
246− -- | Like 'push' but intended for functions that the implementation can consider cheap to compute for performance considerations. WARNING: The function passed to 'pushCheap' may be run multiple times without any caching.
247− pushCheap :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
248− -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
249− -- recomputed whenever any of the read 'Behavior's changes
250− pull :: PullM t a -> Behavior t a
251− -- | Merge a collection of events; the resulting 'Event' will only occur if at
252− -- least one input event is occuring, and will contain all of the input keys
253− -- that are occurring simultaneously
47+ -- | Merge a collection of events; the resulting Event will only occur if at least one input event is occuring, and will contain all of the input keys that are occurring simultaneously
25448 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
255− -- | Efficiently fan-out an event to many destinations. This function should
256− -- be partially applied, and then the result applied repeatedly to create
257− -- child events
49+ -- | Efficiently fan-out an event to many destinations. This function should be partially applied, and then the result applied repeatedly to create child events
25850 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
259− -- | Create an 'Event' that will occur whenever the currently-selected input
260− -- 'Event' occurs
51+ -- | Create an Event that will occur whenever the currently-selected input Event occurs
26152 switch :: Behavior t (Event t a) -> Event t a
262− -- | Create an 'Event' that will occur whenever the input event is occurring
263− -- and its occurrence value, another 'Event', is also occurring
53+ -- | Create an Event that will occur whenever the input event is occurring and its occurrence value, another Event, is also occurring
26454 coincidence :: Event t (Event t a) -> Event t a
265− -- | Extract the 'Behavior' of a 'Dynamic'.
266− current :: Dynamic t a -> Behavior t a
267− -- | Extract the 'Event' of the 'Dynamic'.
268− updated :: Dynamic t a -> Event t a
269− -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
270− -- recent firing of the given 'Event', if any.
271− unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
272− -- | Create a new 'Incremental'. The given 'PullM''s value must always change
273− -- in the same way that the accumulated application of patches would change
274− -- that value.
275− unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
276− -- | Create a merge whose parents can change over time
277− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
278− -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
279− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
280− -- | Extract the 'Behavior' component of an 'Incremental'
281− currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
282− -- | Extract the 'Event' component of an 'Incremental'
283− updatedIncremental :: Patch p => Incremental t p -> Event t p
284− -- | Convert an 'Incremental' to a 'Dynamic'
285− incrementalToDynamic :: Patch p => Incremental t p -> Dynamic t (PatchTarget p)
286− -- | Construct a 'Coercion' for a 'Behavior' given an 'Coercion' for its
287− -- occurrence type
288− behaviorCoercion :: Coercion a b -> Coercion (Behavior t a) (Behavior t b)
289− -- | Construct a 'Coercion' for an 'Event' given an 'Coercion' for its
290− -- occurrence type
291− eventCoercion :: Coercion a b -> Coercion (Event t a) (Event t b)
292− -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
293− -- occurrence type
294− dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
295−#endif
296−
297−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
298−instance t ~ SpiderTimeline Global => Reflex t where
299− newtype Behavior t a = SpiderBehavior { unSpiderBehavior :: Reflex.Spider.Internal.Behavior Global a }
300− newtype Event t a = SpiderEvent { unSpiderEvent :: Reflex.Spider.Internal.Event Global a }
301− newtype Dynamic t a = SpiderDynamic { unSpiderDynamic :: Reflex.Spider.Internal.Dynamic Global (Identity a) } -- deriving (Functor, Applicative, Monad)
302− newtype Incremental t p = SpiderIncremental { unSpiderIncremental :: Reflex.Spider.Internal.Dynamic Global p }
303− type PullM t = SpiderPullM Global
304− type PushM t = SpiderPushM Global
305−never :: Reflex t => Event t a
306−constant :: Reflex t => a -> Behavior t a
307−push :: Reflex t => (a -> PushM t (Maybe b)) -> Event t a -> Event t b
308−pushCheap :: Reflex t => (a -> PushM t (Maybe b)) -> Event t a -> Event t b
309−pull :: Reflex t => PullM t a -> Behavior t a
310−merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
311−fan :: (Reflex t, GCompare k) => Event t (DMap k Identity) -> EventSelector t k
312−switch :: Reflex t => Behavior t (Event t a) -> Event t a
313−coincidence :: Reflex t => Event t (Event t a) -> Event t a
314−current :: Reflex t => Dynamic t a -> Behavior t a
315−updated :: Reflex t => Dynamic t a -> Event t a
316−unsafeBuildDynamic :: Reflex t => PullM t a -> Event t a -> Dynamic t a
317−unsafeBuildIncremental :: (Reflex t, Patch p) => PullM t (PatchTarget p) -> Event t p -> Incremental t p
318−mergeIncremental :: (Reflex t, GCompare k) => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
319−mergeIncrementalWithMove :: (Reflex t, GCompare k) => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
320−currentIncremental :: (Reflex t, Patch p) => Incremental t p -> Behavior t (PatchTarget p)
321−updatedIncremental :: (Reflex t, Patch p) => Incremental t p -> Event t p
322−incrementalToDynamic :: (Reflex t, Patch p) => Incremental t p -> Dynamic t (PatchTarget p)
323−behaviorCoercion :: Reflex t => Coercion a b -> Coercion (Behavior t a) (Behavior t b)
324−eventCoercion :: Reflex t => Coercion a b -> Coercion (Event t a) (Event t b)
325−dynamicCoercion :: Reflex t => Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
326−{-# INLINE never #-}
327−never = SpiderEvent S.eventNever
328−{-# INLINE constant #-}
329−constant = SpiderBehavior . S.behaviorConst
330−{-# INLINE push #-}
331−push f = SpiderEvent . S.push (coerce f) . unSpiderEvent
332−{-# INLINE pushCheap #-}
333−pushCheap f = SpiderEvent . S.pushCheap (coerce f) . unSpiderEvent
334−{-# INLINE pull #-}
335−pull = SpiderBehavior . S.pull . coerce
336−{-# INLINE merge #-}
337−merge = SpiderEvent . S.merge . S.dynamicConst . (coerce :: DMap k (Event (SpiderTimeline x)) -> DMap k (S.Event x))
338−{-# INLINE fan #-}
339−fan e = EventSelector $ SpiderEvent . S.select (S.fan (unSpiderEvent e))
340−{-# INLINE switch #-}
341−switch = SpiderEvent . S.switch . (coerce :: S.Behavior x (Event (SpiderTimeline x) a) -> S.Behavior x (S.Event x a)) . unSpiderBehavior
342−{-# INLINE coincidence #-}
343−coincidence = SpiderEvent . S.coincidence . (coerce :: S.Event x (Event (SpiderTimeline x) a) -> S.Event x (S.Event x a)) . unSpiderEvent
344−{-# INLINE current #-}
345−current = SpiderBehavior . S.dynamicCurrent . unSpiderDynamic
346−{-# INLINE updated #-}
347−updated = coerceEvent . SpiderEvent . dynamicUpdated . unSpiderDynamic
348−{-# INLINE unsafeBuildDynamic #-}
349−unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
350−{-# INLINE unsafeBuildIncremental #-}
351−unsafeBuildIncremental readV0 dv = SpiderIncremental $ S.dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
352−{-# INLINE mergeIncremental #-}
353−mergeIncremental = SpiderEvent . S.merge . (unsafeCoerce :: S.Dynamic x (PatchDMap k (Event (SpiderTimeline x))) -> S.Dynamic x (PatchDMap k (S.Event x))) . unSpiderIncremental
354−{-# INLINE mergeIncrementalWithMove #-}
355−mergeIncrementalWithMove = SpiderEvent . S.mergeWithMove . (unsafeCoerce :: S.Dynamic x (PatchDMapWithMove k (Event (SpiderTimeline x))) -> S.Dynamic x (PatchDMapWithMove k (S.Event x))) . unSpiderIncremental
356−{-# INLINE currentIncremental #-}
357−currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
358−{-# INLINE updatedIncremental #-}
359−updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
360−{-# INLINE incrementalToDynamic #-}
361−incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip S.push (dynamicUpdated i) $ \p -> do
362− c <- readBehaviorUntracked $ dynamicCurrent i
363− return $ Identity <$> apply p c
364−eventCoercion Coercion = Coercion
365−behaviorCoercion Coercion = Coercion
366−dynamicCoercion = unsafeCoerce
367−#endif
368−
369−-- | Coerce a 'Behavior' between representationally-equivalent value types
370−coerceBehavior :: (Reflex t, Coercible a b) => Behavior t a -> Behavior t b
371−coerceBehavior = coerceWith $ behaviorCoercion Coercion
372−
373−-- | Coerce an 'Event' between representationally-equivalent occurrence types
374−coerceEvent :: (Reflex t, Coercible a b) => Event t a -> Event t b
375−coerceEvent = coerceWith $ eventCoercion Coercion
37655
377−-- | Coerce a 'Dynamic' between representationally-equivalent value types
378−coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
379−coerceDynamic = coerceWith $ dynamicCoercion Coercion
380−
381−-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
382−-- _must_ change when and only when the 'Event' fires, such that the
383−-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
384−-- if this is not the case, the resulting 'Dynamic' will behave
385−-- nondeterministically.
386−unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
387−unsafeDynamic = unsafeBuildDynamic . sample
388−
389−-- | Construct a 'Dynamic' value that never changes
390−constDyn :: Reflex t => a -> Dynamic t a
391−constDyn = pure
392−
393−instance (Reflex t, Default a) => Default (Dynamic t a) where
394− def = pure def
395−
396−-- | 'MonadSample' designates monads that can read the current value of a
397−-- 'Behavior'. This includes both 'PullM' and 'PushM'.
39856class (Applicative m, Monad m) => MonadSample t m | m -> t where
399− -- | Get the current value in the 'Behavior'
57+ -- | Get the current value in the Behavior
40058 sample :: Behavior t a -> m a
40159
402−-- | 'MonadHold' designates monads that can create new 'Behavior's based on
403−-- 'Event's; usually this will be 'PushM' or a monad based on it. 'MonadHold'
404−-- is required to create any stateful computations with Reflex.
40560class MonadSample t m => MonadHold t m where
406− -- | Create a new 'Behavior' whose value will initially be equal to the given
407− -- value and will be updated whenever the given 'Event' occurs. The update
408− -- takes effect immediately after the 'Event' occurs; if the occurrence that
409− -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
410− -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
411− -- one.
61+ -- | Create a new Behavior whose value will initially be equal to the given value and will be updated whenever the given Event occurs. The update takes effect immediately after the Event occurs; if the occurrence that sets the Behavior (or one that is simultaneous with it) is used to sample the Behavior, it will see the *old* value of the Behavior, not the new one.
41262 hold :: a -> Event t a -> m (Behavior t a)
413− -- | Create a 'Dynamic' value using the given initial value that changes every
414− -- time the 'Event' occurs.
415− holdDyn :: a -> Event t a -> m (Dynamic t a)
416− -- | Create an 'Incremental' value using the given initial value that changes
417− -- every time the 'Event' occurs.
418− holdIncremental :: Patch p => PatchTarget p -> Event t p -> m (Incremental t p)
419−
420−-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
421−-- key. This is much more efficient than filtering for each key with
422−-- 'fmapMaybe'.
423−newtype EventSelector t k = EventSelector
424− { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
425− -- determined by the type of the key, so this can be used to fan-out
426− -- 'Event's whose sub-'Event's have different types.
427− --
428− -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
429− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
430− -- occurrences of an 'Event'.
431− select :: forall a. k a -> Event t a
432− }
63+
64+newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
43365
43466--------------------------------------------------------------------------------
43567-- Instances
@@ -440,114 +72,85 @@ instance MonadSample t m => MonadSample t (ReaderT r m) where
44072
44173instance MonadHold t m => MonadHold t (ReaderT r m) where
44274 hold a0 = lift . hold a0
443− holdDyn a0 = lift . holdDyn a0
444− holdIncremental a0 = lift . holdIncremental a0
44575
44676instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
44777 sample = lift . sample
44878
44979instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
45080 hold a0 = lift . hold a0
451− holdDyn a0 = lift . holdDyn a0
452− holdIncremental a0 = lift . holdIncremental a0
45381
45482instance MonadSample t m => MonadSample t (StateT s m) where
45583 sample = lift . sample
45684
45785instance MonadHold t m => MonadHold t (StateT s m) where
45886 hold a0 = lift . hold a0
459− holdDyn a0 = lift . holdDyn a0
460− holdIncremental a0 = lift . holdIncremental a0
46187
46288instance MonadSample t m => MonadSample t (ExceptT e m) where
46389 sample = lift . sample
46490
46591instance MonadHold t m => MonadHold t (ExceptT e m) where
46692 hold a0 = lift . hold a0
467− holdDyn a0 = lift . holdDyn a0
468− holdIncremental a0 = lift . holdIncremental a0
46993
47094instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
47195 sample = lift . sample
47296
47397instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
47498 hold a0 = lift . hold a0
475− holdDyn a0 = lift . holdDyn a0
476− holdIncremental a0 = lift . holdIncremental a0
47799
478100instance MonadSample t m => MonadSample t (ContT r m) where
479101 sample = lift . sample
480102
481103instance MonadHold t m => MonadHold t (ContT r m) where
482104 hold a0 = lift . hold a0
483− holdDyn a0 = lift . holdDyn a0
484− holdIncremental a0 = lift . holdIncremental a0
485105
486106--------------------------------------------------------------------------------
487107-- Convenience functions
488108--------------------------------------------------------------------------------
489109
490−-- | Create an 'Event' from another 'Event'. The provided function can sample
491−-- 'Behavior's and hold 'Event's.
110+-- | Create an Event from another Event.
111+-- The provided function can sample 'Behavior's and hold 'Event's.
492112pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
493−pushAlways f = push (fmap Just . f)
113+pushAlways f = push (liftM Just . f)
494114
495115-- | Flipped version of 'fmap'.
496116ffor :: Functor f => f a -> (a -> b) -> f b
497117ffor = flip fmap
498118
119+instance Reflex t => Functor (Behavior t) where
120+ fmap f = pull . liftM f . sample
121+
499122instance Reflex t => Applicative (Behavior t) where
500123 pure = constant
501124 f <*> x = pull $ sample f `ap` sample x
502125 _ *> b = b
503126 a <* _ = a
504127
505−instance Reflex t => Apply (Behavior t) where
506− (<.>) = (<*>)
507−
508−instance Reflex t => Bind (Behavior t) where
509− (>>-) = (>>=)
510−
511−instance (Reflex t, Fractional a) => Fractional (Behavior t a) where
512− (/) = liftA2 (/)
513− fromRational = pure . fromRational
514− recip = fmap recip
515−
516−instance Reflex t => Functor (Behavior t) where
517− fmap f = pull . fmap f . sample
518−
519−instance (Reflex t, IsString a) => IsString (Behavior t a) where
520− fromString = pure . fromString
521−
522128instance Reflex t => Monad (Behavior t) where
523129 a >>= f = pull $ sample a >>= sample . f
524− -- 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.
130+ -- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine Behaviors other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
525131 return = constant
526132 fail = error "Monad (Behavior t) does not support fail"
527133
528−instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
529− mempty = constant mempty
530− mappend a b = pull $ liftM2 mappend (sample a) (sample b)
531− mconcat = pull . fmap mconcat . mapM sample
532−
533−instance (Reflex t, Num a) => Num (Behavior t a) where
534− (+) = liftA2 (+)
535− (-) = liftA2 (-)
536− (*) = liftA2 (*)
537− abs = fmap abs
538− fromInteger = pure . fromInteger
539− negate = fmap negate
540− signum = fmap signum
541−
542134instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
543135 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
544− sconcat = pull . fmap sconcat . mapM sample
136+ sconcat = pull . liftM sconcat . mapM sample
545137#if MIN_VERSION_semigroups(0,17,0)
546138 stimes n = fmap $ stimes n
547139#else
548140 times1p n = fmap $ times1p n
549141#endif
550142
143+instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
144+ mempty = constant mempty
145+ mappend a b = pull $ liftM2 mappend (sample a) (sample b)
146+ mconcat = pull . liftM mconcat . mapM sample
147+
148+--TODO: See if there's a better class in the standard libraries already
149+-- | A class for values that combines filtering and mapping using 'Maybe'.
150+class FunctorMaybe f where
151+ -- | Combined mapping and filtering function.
152+ fmapMaybe :: (a -> Maybe b) -> f a -> f b
153+
551154-- | Flipped version of 'fmapMaybe'.
552155fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
553156fforMaybe = flip fmapMaybe
@@ -557,465 +160,215 @@ fforMaybe = flip fmapMaybe
557160ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
558161ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
559162
560−-- | Left-biased event union (prefers left event on simultaneous
561−-- occurrence).
562−instance Reflex t => Alt (Event t) where
563− ev1 <!> ev2 = leftmost [ev1, ev2]
564−
565−-- | 'Event' intersection (convenient interface to 'coincidence').
566−instance Reflex t => Apply (Event t) where
567− evf <.> evx = coincidence (fmap (<$> evx) evf)
568−
569−-- | 'Event' intersection (convenient interface to 'coincidence').
570−instance Reflex t => Bind (Event t) where
571− evx >>- f = coincidence (f <$> evx)
572− join = coincidence
163+instance Reflex t => FunctorMaybe (Event t) where
164+ fmapMaybe f = push $ return . f
573165
574166instance Reflex t => Functor (Event t) where
575− {-# INLINE fmap #-}
576167 fmap f = fmapMaybe $ Just . f
577168
578−instance Reflex t => FunctorMaybe (Event t) where
579− {-# INLINE fmapMaybe #-}
580− fmapMaybe f = push $ return . f
581−
582−-- | Never: @'zero' = 'never'@.
583−instance Reflex t => Plus (Event t) where
584− zero = never
169+-- | Create a new 'Event' by combining each occurence with the next value
170+-- of the list using the supplied function. If the list runs out of items,
171+-- all subsequent 'Event' occurrences will be ignored.
172+zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
173+zipListWithEvent f l e = do
174+ rec lb <- hold l eTail
175+ let eBoth = flip push e $ \o -> do
176+ l' <- sample lb
177+ return $ case l' of
178+ (h : t) -> Just (f h o, t)
179+ [] -> Nothing
180+ let eTail = fmap snd eBoth
181+ lb `seq` eBoth `seq` eTail `seq` return ()
182+ return $ fmap fst eBoth
585183
586184-- | Replace each occurrence value of the 'Event' with the value of the
587185-- 'Behavior' at the time of that occurrence.
588186tag :: Reflex t => Behavior t b -> Event t a -> Event t b
589187tag b = pushAlways $ \_ -> sample b
590188
591−-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
592−-- current value of the 'Behavior'.
189+-- | Create a new 'Event' that combines occurences of supplied 'Event'
190+-- with the current value of the 'Behavior'.
593191attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
594192attach = attachWith (,)
595193
596−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
597−-- combining it with the current value of the 'Behavior'.
194+-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
195+-- by combining it with the current value of the 'Behavior'.
598196attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
599197attachWith f = attachWithMaybe $ \a b -> Just $ f a b
600198
601−-- | Create a new 'Event' by combining each occurence with the current value of
602−-- the 'Behavior'. The occurrence is discarded if the combining function returns
603−-- Nothing
199+-- | Create a new 'Event' by combining each occurence with the current
200+-- value of the 'Behavior'. The occurrence is discarded if the combining function
201+-- returns Nothing
604202attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
605−attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
203+attachWithMaybe f b e = flip push e $ \o -> liftM (flip f o) $ sample b
606204
607−-- | Create a new 'Event' that only occurs only once, on the first occurence of
205+-- | Alias for 'headE'
206+onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
207+onceE = headE
208+
209+-- | Create a new 'Event' that only occurs on the first occurence of
608210-- the supplied 'Event'.
609211headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
610212headE e = do
611213 rec be <- hold e $ fmap (const never) e'
612214 let e' = switch be
215+ e' `seq` return ()
613216 return e'
614217
615−-- | Create a new 'Event' that occurs on all but the first occurence of the
616−-- supplied 'Event'.
218+-- | Create a new 'Event' that occurs on all but the first occurence
219+-- of the supplied 'Event'.
617220tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
618−tailE e = snd <$> headTailE e
221+tailE e = liftM snd $ headTailE e
619222
620−-- | Create a tuple of two 'Event's with the first one occuring only the first
621−-- time the supplied 'Event' occurs and the second occuring on all but the first
622−-- occurence.
223+-- | Create a tuple of two 'Event's with the first one occuring only
224+-- the first time the supplied 'Event' occurs and the second occuring
225+-- on all but the first occurence.
623226headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
624227headTailE e = do
625228 eHead <- headE e
626229 be <- hold never $ fmap (const e) eHead
627230 return (eHead, switch be)
628231
629−-- | Split the supplied 'Event' into two individual 'Event's occuring at the
630−-- same time with the respective values from the tuple.
232+-- | Split the supplied 'Event' into two individual 'Event's occuring
233+-- at the same time with the respective values from the tuple.
631234splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
632235splitE e = (fmap fst e, fmap snd e)
633236
634237-- | Print the supplied 'String' and the value of the 'Event' on each
635238-- occurence. This should /only/ be used for debugging.
636239--
637−-- Note: As with Debug.Trace.trace, the message will only be printed if the
638−-- 'Event' is actually used.
240+-- Note: As with Debug.Trace.trace, the message will only be printed if
241+-- the 'Event' is actually used.
639242traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
640243traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
641244
642−-- | Print the output of the supplied function on each occurence of the
643−-- 'Event'. This should /only/ be used for debugging.
245+-- | Print the output of the supplied function on each occurence of
246+-- the 'Event'. This should /only/ be used for debugging.
644247--
645−-- Note: As with Debug.Trace.trace, the message will only be printed if the
646−-- 'Event' is actually used.
248+-- Note: As with Debug.Trace.trace, the message will only be printed if
249+-- the 'Event' is actually used.
647250traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
648251traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
649252
650−instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
651− (<>) = alignWith (mergeThese (<>))
652− sconcat = fmap sconcat . mergeList . toList
653−#if MIN_VERSION_semigroups(0,17,0)
654− stimes n = fmap $ stimes n
655−#else
656− times1p n = fmap $ times1p n
657−#endif
253+-- | Tag type for 'Either' to use it as a 'DSum'.
254+data EitherTag l r a where
255+ LeftTag :: EitherTag l r l
256+ RightTag :: EitherTag l r r
257+
258+instance GEq (EitherTag l r) where
259+ geq a b = case (a, b) of
260+ (LeftTag, LeftTag) -> Just Refl
261+ (RightTag, RightTag) -> Just Refl
262+ _ -> Nothing
263+
264+instance GCompare (EitherTag l r) where
265+ gcompare a b = case (a, b) of
266+ (LeftTag, LeftTag) -> GEQ
267+ (LeftTag, RightTag) -> GLT
268+ (RightTag, LeftTag) -> GGT
269+ (RightTag, RightTag) -> GEQ
270+
271+instance GShow (EitherTag l r) where
272+ gshowsPrec _ a = case a of
273+ LeftTag -> showString "LeftTag"
274+ RightTag -> showString "RightTag"
275+
276+instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
277+ showTaggedPrec t n (Identity a) = case t of
278+ LeftTag -> showsPrec n a
279+ RightTag -> showsPrec n a
280+
281+-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
282+eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
283+eitherToDSum = \case
284+ Left a -> (LeftTag :=> Identity a)
285+ Right b -> (RightTag :=> Identity b)
286+
287+-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
288+dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
289+dsumToEither = \case
290+ (LeftTag :=> Identity a) -> Left a
291+ (RightTag :=> Identity b) -> Right b
292+
293+-- | Extract the values of a 'DMap' of 'EitherTag's.
294+dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
295+dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
296+ (Nothing, Nothing) -> Nothing
297+ (Just (Identity a), Nothing) -> Just $ This a
298+ (Nothing, Just (Identity b)) -> Just $ That b
299+ (Just (Identity a), Just (Identity b)) -> Just $ These a b
300+
301+-- | Create a new 'Event' that occurs if at least one of the supplied
302+-- 'Event's occurs. If both occur at the same time they are combined
303+-- using 'mappend'.
304+appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
305+appendEvents e1 e2 = mergeThese mappend <$> align e1 e2
306+
307+{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
308+sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
309+sequenceThese t = case t of
310+ This ma -> liftM This ma
311+ These ma mb -> liftM2 These ma mb
312+ That mb -> liftM That mb
658313
659314instance (Semigroup a, Reflex t) => Monoid (Event t a) where
660315 mempty = never
661− mappend = (<>)
316+ mappend a b = mconcat [a, b]
662317 mconcat = fmap sconcat . mergeList
663318
664−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
665−-- list occurs. If multiple occur at the same time they are folded from the left
666−-- with the given function.
667−{-# INLINE mergeWith #-}
319+-- | Create a new 'Event' that occurs if at least one of the 'Event's
320+-- in the list occurs. If multiple occur at the same time they are
321+-- folded from the left with the given function.
668322mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
669−mergeWith = mergeWith' id
670−
671−{-# INLINE mergeWith' #-}
672−mergeWith' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
673−mergeWith' f g es = fmap (Prelude.foldl1 g . map (\(Const2 _ :=> Identity v) -> f v) . DMap.toList)
674− . merge
675− . DMap.fromDistinctAscList
676− . map (\(k, v) -> Const2 k :=> v)
677− $ zip [0 :: Int ..] es
678−
679−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
680−-- list occurs. If multiple occur at the same time the value is the value of the
681−-- leftmost event.
682−{-# INLINE leftmost #-}
323+mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
324+ . merge
325+ . DMap.fromDistinctAscList
326+ . map (\(k, v) -> (Const2 k) :=> v)
327+ $ zip [0 :: Int ..] es
328+
329+-- | Create a new 'Event' that occurs if at least one of the 'Event's
330+-- in the list occurs. If multiple occur at the same time the value is
331+-- the value of the leftmost event.
683332leftmost :: Reflex t => [Event t a] -> Event t a
684333leftmost = mergeWith const
685334
686−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
687−-- list occurs and has a list of the values of all 'Event's occuring at that
688−-- time.
335+-- | Create a new 'Event' that occurs if at least one of the 'Event's
336+-- in the list occurs and has a list of the values of all 'Event's
337+-- occuring at that time.
689338mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
690339mergeList [] = never
691−mergeList es = mergeWithFoldCheap' id es
692−
693−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
694−unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
340+mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
695341
696−-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
697−-- occurs if at least one of them occurs and has a map of values of all 'Event's
698−-- occuring at that time.
342+-- | Create a new 'Event' combining the map of 'Event's into an
343+-- 'Event' that occurs if at least one of them occurs and has a map of
344+-- values of all 'Event's occuring at that time.
699345mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
700346mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
701347
702−-- | Create a merge whose parents can change over time
703−mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
704−mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
705−
706−-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
707−mergeMapIncrementalWithMove :: (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Event t a)) -> Event t (Map k a)
708−mergeMapIncrementalWithMove = fmap dmapToMap . mergeIncrementalWithMove . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWithMoveWith id)
709−
710−-- | Split the event into separate events for 'Left' and 'Right' values.
711−fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
712−fanEither e =
713− let justLeft = either Just (const Nothing)
714− justRight = either (const Nothing) Just
715− in (fmapMaybe justLeft e, fmapMaybe justRight e)
716−
717−-- | Split the event into separate events for 'This' and 'That' values,
718−-- allowing them to fire simultaneously when the input value is 'These'.
719−fanThese :: Reflex t => Event t (These a b) -> (Event t a, Event t b)
720−fanThese e =
721− let this (This x) = Just x
722− this (These x _) = Just x
723− this _ = Nothing
724− that (That y) = Just y
725− that (These _ y) = Just y
726− that _ = Nothing
727− in (fmapMaybe this e, fmapMaybe that e)
728−
729−-- | Split the event into an 'EventSelector' that allows efficient selection of
730−-- the individual 'Event's.
348+-- | Split the event into an 'EventSelector' that allows efficient
349+-- selection of the individual 'Event's.
731350fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
732351fanMap = fan . fmap mapToDMap
733352
734−-- | Switches to the new event whenever it receives one. Whenever a new event
735−-- is provided, if it is firing, its value will be the resulting event's value;
736−-- if it is not firing, but the old one is, the old one's value will be used.
737−switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
353+-- | Switches to the new event whenever it receives one; the new event is used immediately, on the same frame that it is switched to
354+switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
738355switchPromptly ea0 eea = do
739356 bea <- hold ea0 eea
740357 let eLag = switch bea
741358 eCoincidences = coincidence eea
742359 return $ leftmost [eCoincidences, eLag]
743360
744−-- | 'switch'es to a new event whenever it receives one. At the moment of
745−-- switching, the old event will be ignored if it fires, and the new one will be
746−-- used if it fires; this is the opposite of 'switch', which will use only the
747−-- old value.
748−switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
749−switchPromptOnly e0 e' = do
750− eLag <- switch <$> hold e0 e'
751− return $ coincidence $ leftmost [e', eLag <$ eLag]
752−
753361instance Reflex t => Align (Event t) where
754362 nil = never
755− align = alignEventWithMaybe Just
363+ align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
756364
757−-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
758−-- the 'Behavior' is true at the time of occurence.
365+-- | Create a new 'Event' that only occurs if the supplied 'Event'
366+-- occurs and the 'Behavior' is true at the time of occurence.
759367gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
760368gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
761369
762−-- | Create a new behavior given a starting behavior and switch to a the behvior
763−-- carried by the event when it fires.
370+-- | Create a new behavior given a starting behavior and switch to a the
371+-- behvior carried by the event when it fires.
764372switcher :: (Reflex t, MonadHold t m)
765373 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
766374switcher b eb = pull . (sample <=< sample) <$> hold b eb
767−
768−-- | Combine two 'Dynamic's. The result will change whenever either (or both)
769−-- input 'Dynamic' changes. Equivalent to @zipDynWith (,)@.
770−zipDyn :: Reflex t => Dynamic t a -> Dynamic t b -> Dynamic t (a, b)
771−zipDyn = zipDynWith (,)
772−
773−-- | Combine two 'Dynamic's with a combining function. The result will change
774−-- whenever either (or both) input 'Dynamic' changes.
775−-- More efficient than liftA2.
776−zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
777−zipDynWith f da db =
778− let eab = align (updated da) (updated db)
779− ec = flip push eab $ \o -> do
780− (a, b) <- case o of
781− This a -> do
782− b <- sample $ current db
783− return (a, b)
784− That b -> do
785− a <- sample $ current da
786− return (a, b)
787− These a b -> return (a, b)
788− return $ Just $ f a b
789− in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
790−
791−instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
792− mconcat = distributeListOverDynWith mconcat
793− mempty = constDyn mempty
794− mappend = zipDynWith mappend
795−
796−-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
797−-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
798−-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
799−distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
800−distributeDMapOverDynPure dm = case DMap.toList dm of
801− [] -> constDyn DMap.empty
802− [k :=> v] -> fmap (DMap.singleton k . Identity) v
803− _ ->
804− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
805− edmPre = merge $ DMap.map updated dm
806− result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
807− olds <- sample $ current result
808− return $ DMap.unionWithKey (\_ _ new -> new) olds news
809− in result
810−
811−-- | Convert a list of 'Dynamic's into a 'Dynamic' list.
812−distributeListOverDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
813−distributeListOverDyn = distributeListOverDynWith id
814−
815−-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
816−distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
817−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
818−
819−-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
820−-- unless the second supplied 'Event' occurs simultaneously.
821−difference :: Reflex t => Event t a -> Event t b -> Event t a
822−difference = alignEventWithMaybe $ \case
823− This a -> Just a
824− _ -> Nothing
825−
826−alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
827−alignEventWithMaybe f ea eb =
828− fmapMaybe (f <=< dmapToThese)
829− $ merge
830− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
831−
832−
833−--------------------------------------------------------------------------------
834−-- Accumulator
835−--------------------------------------------------------------------------------
836−
837−-- | An 'Accumulator' type can be built by accumulating occurrences of an
838−-- 'Event'.
839−class Reflex t => Accumulator t f | f -> t where
840− accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
841− accum f = accumMaybe $ \v o -> Just $ f v o
842− accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
843− accumM f = accumMaybeM $ \v o -> Just <$> f v o
844− accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
845− accumMaybe f = accumMaybeM $ \v o -> return $ f v o
846− accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
847− mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
848− mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
849− mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
850− mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
851− mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
852− mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
853− mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
854−
855−-- | Accumulate occurrences of an 'Event', producing an output occurrence each
856−-- time. Discard the underlying 'Accumulator'.
857−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)
858−mapAccum_ f z e = do
859− (_ :: Behavior t a, result) <- mapAccum f z e
860− return result
861−
862−-- | Accumulate occurrences of an 'Event', possibly producing an output
863−-- occurrence each time. Discard the underlying 'Accumulator'.
864−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)
865−mapAccumMaybe_ f z e = do
866− (_ :: Behavior t a, result) <- mapAccumMaybe f z e
867− return result
868−
869−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
870−-- an output occurrence each time. Discard the underlying 'Accumulator'.
871−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)
872−mapAccumM_ f z e = do
873− (_ :: Behavior t a, result) <- mapAccumM f z e
874− return result
875−
876−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
877−-- producing an output occurrence each time. Discard the underlying
878−-- 'Accumulator'.
879−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)
880−mapAccumMaybeM_ f z e = do
881− (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
882− return result
883−
884−instance Reflex t => Accumulator t (Dynamic t) where
885− accumMaybeM f z e = do
886− rec let e' = flip push e $ \o -> do
887− v <- sample $ current d'
888− f v o
889− d' <- holdDyn z e'
890− return d'
891− mapAccumMaybeM f z e = do
892− rec let e' = flip push e $ \o -> do
893− v <- sample $ current d'
894− result <- f v o
895− return $ case result of
896− (Nothing, Nothing) -> Nothing
897− _ -> Just result
898− d' <- holdDyn z $ fmapMaybe fst e'
899− return (d', fmapMaybe snd e')
900−
901−instance Reflex t => Accumulator t (Behavior t) where
902− accumMaybeM f z e = current <$> accumMaybeM f z e
903− mapAccumMaybeM f z e = first current <$> mapAccumMaybeM f z e
904−
905−instance Reflex t => Accumulator t (Event t) where
906− accumMaybeM f z e = updated <$> accumMaybeM f z e
907− mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
908−
909−-- | Create a new 'Event' by combining each occurence with the next value of the
910−-- list using the supplied function. If the list runs out of items, all
911−-- subsequent 'Event' occurrences will be ignored.
912−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
913−zipListWithEvent f l e = do
914− let f' a b = case a of
915− h:t -> (Just t, Just $ f h b)
916− _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
917− mapAccumMaybe_ f' l e
918−
919−-- | A 'Monad' that supports adjustment over time. After an action has been
920−-- run, if the given events fire, it will adjust itself so that its net effect
921−-- is as though it had originally been run with the new value. Note that there
922−-- is some issue here with persistent side-effects: obviously, IO (and some
923−-- other side-effects) cannot be undone, so it is up to the instance implementer
924−-- to determine what the best meaning for this class is in such cases.
925−class (Reflex t, Monad m) => MonadAdjust t m | m -> t where
926− runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
927− traverseDMapWithKeyWithAdjust :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> m (DMap k v', Event t (PatchDMap k v'))
928− traverseDMapWithKeyWithAdjustWithMove :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> m (DMap k v', Event t (PatchDMapWithMove k v'))
929−
930−instance (Reflex t, MonadAdjust t m) => MonadAdjust t (ReaderT r m) where
931− runWithReplace a0 a' = do
932− r <- ask
933− lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
934− traverseDMapWithKeyWithAdjust f dm0 dm' = do
935− r <- ask
936− lift $ traverseDMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
937− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
938− r <- ask
939− lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
940−
941−sequenceDMapWithAdjust :: (GCompare k, MonadAdjust t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
942−sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
943−
944−sequenceDMapWithAdjustWithMove :: (GCompare k, MonadAdjust t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
945−sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
946−
947−mapMapWithAdjustWithMove :: forall t m k v v'. (MonadAdjust t m, Ord k) => (k -> v -> m v') -> Map k v -> Event t (PatchMapWithMove k v) -> m (Map k v', Event t (PatchMapWithMove k v'))
948−mapMapWithAdjustWithMove f m0 m' = do
949− (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
950− return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
951−
952−------------------
953−-- Cheap Functions
954−------------------
955−
956−{-# INLINE pushAlwaysCheap #-}
957−pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
958−pushAlwaysCheap f = pushCheap (fmap Just . f)
959−
960−{-# INLINE fmapMaybeCheap #-}
961−fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
962−fmapMaybeCheap f = pushCheap $ return . f
963−
964−{-# INLINE fforMaybeCheap #-}
965−fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
966−fforMaybeCheap = flip fmapMaybeCheap
967−
968−{-# INLINE fforCheap #-}
969−fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
970−fforCheap = flip fmapCheap
971−
972−{-# INLINE fmapCheap #-}
973−fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
974−fmapCheap f = pushCheap $ return . Just . f
975−
976−{-# INLINE tagCheap #-}
977−tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
978−tagCheap b = pushAlwaysCheap $ \_ -> sample b
979−
980−{-# INLINE mergeWithCheap #-}
981−mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
982−mergeWithCheap = mergeWithCheap' id
983−
984−{-# INLINE mergeWithCheap' #-}
985−mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
986−mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
987−
988−{-# INLINE mergeWithFoldCheap' #-}
989−mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
990−mergeWithFoldCheap' f es =
991− fmapCheap (f . (\(h : t) -> h :| t) . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
992− . merge
993− . DMap.fromDistinctAscList
994− . map (\(k, v) -> Const2 k :=> v)
995− $ zip [0 :: Int ..] es
996−
997−--------------------------------------------------------------------------------
998−-- Deprecated functions
999−--------------------------------------------------------------------------------
1000−
1001−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
1002−-- occurs. If both occur at the same time they are combined using 'mappend'.
1003−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
1004−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
1005−appendEvents = alignWith $ mergeThese mappend
1006−
1007−-- | Alias for 'headE'
1008−{-# DEPRECATED onceE "Use 'headE' instead" #-}
1009−onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
1010−onceE = headE
1011−
1012−-- | Run both sides of a 'These' monadically, combining the results.
1013−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
1014−#ifdef USE_TEMPLATE_HASKELL
1015−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
1016−#endif
1017−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
1018−sequenceThese t = case t of
1019− This ma -> fmap This ma
1020− These ma mb -> liftM2 These ma mb
1021− That mb -> fmap That mb
src/Reflex/Dynamic.hs+324−388
@@ -1,141 +1,186 @@
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− , RebuildSortedHList (..)
55− , IsHList (..)
56− , AllAreFunctors (..)
57− , HListPtr (..)
58− , distributeFHListOverDynPure
59− -- Unsafe
60− , unsafeDynamic
61− -- * Deprecated functions
62− , apDyn
63− , attachDyn
64− , attachDynWith
65− , attachDynWithMaybe
66− , collectDyn
67− , combineDyn
68− , distributeDMapOverDyn
69− , distributeFHListOverDyn
70− , forDyn
71− , forDynM
72− , getDemuxed
73− , joinDyn
74− , mapDyn
75− , mapDynM
76− , mconcatDyn
77− , nubDyn
78− , splitDyn
79− , tagDyn
80− ) where
1+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, DataKinds, PolyKinds #-}
2+module Reflex.Dynamic ( Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
3+ , current
4+ , updated
5+ , constDyn
6+ , holdDyn
7+ , nubDyn
8+ , count
9+ , toggle
10+ , switchPromptlyDyn
11+ , tagDyn
12+ , attachDyn
13+ , attachDynWith
14+ , attachDynWithMaybe
15+ , mapDyn
16+ , forDyn
17+ , mapDynM
18+ , foldDyn
19+ , foldDynM
20+ , foldDynMaybe
21+ , foldDynMaybeM
22+ , combineDyn
23+ , collectDyn
24+ , mconcatDyn
25+ , distributeDMapOverDyn
26+ , joinDyn
27+ , joinDynThroughMap
28+ , traceDyn
29+ , traceDynWith
30+ , splitDyn
31+ , Demux
32+ , demux
33+ , getDemuxed
34+ -- Things that probably aren't very useful:
35+ , HList (..)
36+ , FHList (..)
37+ , distributeFHListOverDyn
38+ -- Unsafe
39+ , unsafeDynamic
40+ ) where
8141
8242import Prelude hiding (mapM, mapM_)
8343
84−import Data.Functor.Misc
8544import Reflex.Class
45+import Data.Functor.Misc
8646
87−import Control.Applicative ((<*>))
88−import Control.Monad hiding (forM, forM_, mapM, mapM_)
47+import Control.Monad hiding (mapM, mapM_, forM, forM_)
8948import Control.Monad.Fix
90−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
49+import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_)
50+import Data.These
51+import Data.Traversable (mapM, forM)
9152import Data.Align
53+import Data.Map (Map)
54+import qualified Data.Map as Map
9255import Data.Dependent.Map (DMap)
9356import qualified Data.Dependent.Map as DMap
9457import Data.Dependent.Sum (DSum (..))
95−import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
96−import Data.Map (Map)
58+import Data.GADT.Compare (GCompare (..), GEq (..), (:~:) (..), GOrdering (..))
9759import Data.Monoid
98−import Data.These
60+--import Data.HList (HList (..), hBuild)
9961
100−import Debug.Trace
62+data HList (l::[*]) where
63+ HNil :: HList '[]
64+ HCons :: e -> HList l -> HList (e ': l)
10165
102−-- | Create a new 'Dynamic' that only signals changes if the values actually
103−-- changed.
104−uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
105−uniqDyn = uniqDynBy (==)
66+infixr 2 `HCons`
10667
107−-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
108−-- changes and the given function returns 'False' when given both the old and
109−-- the new values.
110−uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
111−uniqDynBy eq d =
112− let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
113− in unsafeDynamic (current d) e'
68+type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
69+type instance HRevApp '[] l = l
70+type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
71+
72+hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
73+hRevApp HNil l = l
74+hRevApp (HCons x l) l' = hRevApp l (HCons x l')
11475
115−-- | Create a 'Dynamic' using the initial value and change it each time the
116−-- 'Event' occurs using a folding function on the previous value and the value
117−-- of the 'Event'.
76+hReverse :: HList l -> HList (HRevApp l '[])
77+hReverse l = hRevApp l HNil
78+
79+hBuild :: (HBuild' '[] r) => r
80+hBuild = hBuild' HNil
81+
82+class HBuild' l r where
83+ hBuild' :: HList l -> r
84+
85+instance (l' ~ HRevApp l '[])
86+ => HBuild' l (HList l') where
87+ hBuild' l = hReverse l
88+
89+instance HBuild' (a ': l) r
90+ => HBuild' l (a->r) where
91+ hBuild' l x = hBuild' (HCons x l)
92+
93+-- | A container for a value that can change over time and allows notifications on changes.
94+-- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
95+-- change if and only if the Event fires.
96+--
97+-- Although @Dynamic@ logically has a 'Functor' instance, currently it can\'t
98+-- be implemented without potentially requiring the mapped function to be
99+-- evaluated twice. Instead, you can use 'mapDyn', which runs in a 'MonadHold'
100+-- instance, to achieve the same result.
101+data Dynamic t a
102+ = Dynamic (Behavior t a) (Event t a)
103+
104+unsafeDynamic :: Behavior t a -> Event t a -> Dynamic t a
105+unsafeDynamic = Dynamic
106+
107+-- | Extract the 'Behavior' of a 'Dynamic'.
108+current :: Dynamic t a -> Behavior t a
109+current (Dynamic b _) = b
110+
111+-- | Extract the 'Event' of the 'Dynamic'.
112+updated :: Dynamic t a -> Event t a
113+updated (Dynamic _ e) = e
114+
115+-- | 'Dynamic' with the constant supplied value.
116+constDyn :: Reflex t => a -> Dynamic t a
117+constDyn x = Dynamic (constant x) never
118+
119+-- | Create a 'Dynamic' using the initial value that changes every
120+-- time the 'Event' occurs.
121+holdDyn :: MonadHold t m => a -> Event t a -> m (Dynamic t a)
122+holdDyn v0 e = do
123+ b <- hold v0 e
124+ return $ Dynamic b e
125+
126+-- | Create a new 'Dynamic' that only signals changes if the values
127+-- actually changed.
128+nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
129+nubDyn d =
130+ let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
131+ in Dynamic (current d) e' --TODO: Avoid invalidating the outgoing Behavior
132+
133+{-
134+instance Reflex t => Functor (Dynamic t) where
135+ fmap f d =
136+ let e' = fmap f $ updated d
137+ eb' = push (\b' -> liftM Just $ constant b') e'
138+ b0 = fmap f $ current d
139+
140+-}
141+
142+-- | Map a function over a 'Dynamic'.
143+mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
144+mapDyn f = mapDynM $ return . f
145+
146+-- | Flipped version of 'mapDyn'.
147+forDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
148+forDyn = flip mapDyn
149+
150+-- | Map a monadic function over a 'Dynamic'. The only monadic action that the given function can
151+-- perform is 'sample'.
152+{-# INLINE mapDynM #-}
153+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)
154+mapDynM f d = do
155+ let e' = push (liftM Just . f :: a -> PushM t (Maybe b)) $ updated d
156+ eb' = fmap constant e'
157+ v0 = pull $ f =<< sample (current d)
158+ bb' :: Behavior t (Behavior t b) <- hold v0 eb'
159+ let b' = pull $ sample =<< sample bb'
160+ return $ Dynamic b' e'
161+
162+-- | Create a 'Dynamic' using the initial value and change it each
163+-- time the 'Event' occurs using a folding function on the previous
164+-- value and the value of the 'Event'.
118165foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
119−foldDyn = accum . flip
166+foldDyn f = foldDynMaybe $ \o v -> Just $ f o v
120167
121−-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
122−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
168+-- | Create a 'Dynamic' using the initial value and change it each
169+-- time the 'Event' occurs using a monadic folding function on the
170+-- previous value and the value of the 'Event'.
123171foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
124−foldDynM = accumM . flip
125−
126−-- | Create a 'Dynamic' using the provided initial value and change it each time
127−-- the provided 'Event' occurs, using a function to combine the old value with
128−-- the 'Event''s value. If the function returns 'Nothing', the value is not
129−-- changed; this is distinct from returning 'Just' the old value, since the
130−-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
131−-- in the 'Nothing' case.
172+foldDynM f = foldDynMaybeM $ \o v -> liftM Just $ f o v
173+
132174foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
133−foldDynMaybe = accumMaybe . flip
175+foldDynMaybe f = foldDynMaybeM $ \o v -> return $ f o v
134176
135−-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
136−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
137177foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
138−foldDynMaybeM = accumMaybeM . flip
178+foldDynMaybeM f z e = do
179+ rec let e' = flip push e $ \o -> do
180+ v <- sample b'
181+ f o v
182+ b' <- hold z e'
183+ return $ Dynamic b' e'
139184
140185-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
141186count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
@@ -146,48 +191,124 @@ count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
146191toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
147192toggle = foldDyn (const not)
148193
149−-- | Switches to the new 'Event' whenever it receives one. Switching occurs
150−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
151−-- old and new inner Events fire simultaneously, the output will fire with the
152−-- value of the *new* 'Event'.
194+-- | Switches to the new 'Event' whenever it receives one. Switching
195+-- occurs *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the old and new
196+-- inner Events fire simultaneously, the output will fire with the value of the *new* 'Event'.
153197switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
154198switchPromptlyDyn de =
155199 let eLag = switch $ current de
156200 eCoincidences = coincidence $ updated de
157201 in leftmost [eCoincidences, eLag]
158202
159−-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
160−splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
161−splitDynPure d = (fmap fst d, fmap snd d)
162−
163−-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
164−-- non-'Dynamic' elements.
165−distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
166−distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
167−
168−-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
169−-- non-'Dynamic' elements, preserving the order of the elements.
170−distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
171−distributeListOverDynPure =
172− fmap (map fromDSum . DMap.toAscList) .
173− distributeDMapOverDynPure .
174− DMap.fromDistinctAscList .
175− zipWith toDSum [0..]
176− where
177− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
178− toDSum k v = Const2 k :=> v
179− fromDSum :: DSum (Const2 Int a) Identity -> a
180− fromDSum (Const2 _ :=> Identity v) = v
203+{-
204+
205+mergeEventsWith :: Reflex t m => (a -> a -> a) -> Event t a -> Event t a -> m (Event t a)
206+mergeEventsWith f ea eb = mapE (mergeThese f) =<< alignEvents ea eb
207+
208+firstE :: (Reflex t m) => [Event t a] -> m (Event t a)
209+firstE [] = return never
210+firstE (h:t) = mergeEventsLeftBiased h =<< firstE t
211+
212+concatEventsWith :: (Reflex t m) => (a -> a -> a) -> [Event t a] -> m (Event t a)
213+concatEventsWith _ [] = return never
214+concatEventsWith _ [e] = return e
215+concatEventsWith f es = mapEM (liftM (foldl1 f . map (\(Const2 _ :=> v) -> v) . DMap.toList) . sequenceDmap) <=< mergeEventDMap $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
216+--concatEventsWith f (h:t) = mergeEventsWith f h =<< concatEventsWith f t
217+
218+mconcatE :: (Reflex t m, Monoid a) => [Event t a] -> m (Event t a)
219+mconcatE = concatEventsWith mappend
220+-}
221+
222+-- | Split the 'Dynamic' into two 'Dynamic's, each taking the
223+-- respective value of the tuple.
224+splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
225+splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
226+
227+-- | Merge the 'Dynamic' values using their 'Monoid' instance.
228+mconcatDyn :: forall t m a. (Reflex t, MonadHold t m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
229+mconcatDyn es = do
230+ ddm :: Dynamic t (DMap (Const2 Int a) Identity) <- distributeDMapOverDyn . DMap.fromList . map (\(k, v) -> Const2 k :=> v) $ zip [0 :: Int ..] es
231+ mapDyn (mconcat . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) ddm
232+
233+-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of
234+-- Dynamic values.
235+distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
236+distributeDMapOverDyn dm = case DMap.toList dm of
237+ [] -> return $ constDyn DMap.empty
238+ [k :=> v] -> mapDyn (DMap.singleton k . Identity) v
239+ _ -> do
240+ let edmPre = merge $ rewrapDMap updated dm
241+ edm :: Event t (DMap k Identity) = flip push edmPre $ \o -> return . Just =<< do
242+ let f _ = \case
243+ This origDyn -> sample $ current origDyn
244+ That _ -> error "distributeDMapOverDyn: should be impossible to have an event occurring that is not present in the original DMap"
245+ These _ (Identity newVal) -> return newVal
246+ sequenceDmap $ combineDMapsWithKey f dm (wrapDMap Identity o)
247+ dm0 :: Behavior t (DMap k Identity) = pull $ do
248+ liftM DMap.fromList . forM (DMap.toList dm) $ \(k :=> dv) -> liftM ((k :=>) . Identity) . sample $ current dv
249+ bbdm :: Behavior t (Behavior t (DMap k Identity)) <- hold dm0 $ fmap constant edm
250+ let bdm = pull $ sample =<< sample bbdm
251+ return $ Dynamic bdm edm
252+
253+-- | Merge two 'Dynamic's into a new one using the provided
254+-- function. The new 'Dynamic' changes its value each time one of the
255+-- original 'Dynamic's changes its value.
256+combineDyn :: forall t m a b c. (Reflex t, MonadHold t m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
257+combineDyn f da db = do
258+ let eab = align (updated da) (updated db)
259+ ec = flip push eab $ \o -> do
260+ (a, b) <- case o of
261+ This a -> do
262+ b <- sample $ current db
263+ return (a, b)
264+ That b -> do
265+ a <- sample $ current da
266+ return (a, b)
267+ These a b -> return (a, b)
268+ return $ Just $ f a b
269+ c0 :: Behavior t c = pull $ liftM2 f (sample $ current da) (sample $ current db)
270+ bbc :: Behavior t (Behavior t c) <- hold c0 $ fmap constant ec
271+ let bc :: Behavior t c = pull $ sample =<< sample bbc
272+ return $ Dynamic bc ec
273+
274+{-
275+tagInnerDyn :: Reflex t => Event t (Dynamic t a) -> Event t a
276+tagInnerDyn e =
277+ let eSlow = push (liftM Just . sample . current) e
278+ eFast = coincidence $ fmap updated e
279+ in leftmost [eFast, eSlow]
280+-}
281+
282+-- | Join a nested 'Dynamic' into a new 'Dynamic' that has the value
283+-- of the inner 'Dynamic'.
284+joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
285+joinDyn dd =
286+ let b' = pull $ sample . current =<< sample (current dd)
287+ eOuter :: Event t a = pushAlways (sample . current) $ updated dd
288+ eInner :: Event t a = switch $ fmap updated (current dd)
289+ eBoth :: Event t a = coincidence $ fmap updated (updated dd)
290+ e' = leftmost [eBoth, eOuter, eInner]
291+ in Dynamic b' e'
181292
182293--TODO: Generalize this to functors other than Maps
183294-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
184295-- with the current values of the 'Dynamic's in a map.
185296joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
186−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
187−
188−-- | Print the value of the 'Dynamic' when it is first read and on each
189−-- subsequent change that is observed (as traceEvent), prefixed with the
190−-- provided string. This should /only/ be used for debugging.
297+joinDynThroughMap dd =
298+ let b' = pull $ mapM (sample . current) =<< sample (current dd)
299+ eOuter :: Event t (Map k a) = pushAlways (mapM (sample . current)) $ updated dd
300+ eInner :: Event t (Map k a) = attachWith (flip Map.union) b' $ switch $ fmap (mergeMap . fmap updated) (current dd) --Note: the flip is important because Map.union is left-biased
301+ readNonFiring :: MonadSample t m => These (Dynamic t a) a -> m a
302+ readNonFiring = \case
303+ This d -> sample $ current d
304+ That a -> return a
305+ These _ a -> return a
306+ eBoth :: Event t (Map k a) = coincidence $ fmap (\m -> pushAlways (mapM readNonFiring . align m) $ mergeMap $ fmap updated m) (updated dd)
307+ e' = leftmost [eBoth, eOuter, eInner]
308+ in Dynamic b' e'
309+
310+-- | Print the value of the 'Dynamic' on each change and prefix it
311+-- with the provided string. This should /only/ be used for debugging.
191312--
192313-- Note: Just like Debug.Trace.trace, the value will only be shown if something
193314-- else in the system is depending on it.
@@ -195,8 +316,7 @@ traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
195316traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
196317
197318-- | Print the result of applying the provided function to the value
198−-- of the 'Dynamic' when it is first read and on each subsequent change
199−-- that is observed (as traceEvent). This should /only/ be used for
319+-- of the 'Dynamic' on each change. This should /only/ be used for
200320-- debugging.
201321--
202322-- Note: Just like Debug.Trace.trace, the value will only be shown if something
@@ -204,46 +324,42 @@ traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
204324traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
205325traceDynWith f d =
206326 let e' = traceEventWith f $ updated d
207− getV0 = do
208− x <- sample $ current d
209− trace (f x) $ return x
210− in unsafeBuildDynamic getV0 e'
327+ in Dynamic (current d) e'
211328
212329-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
213330-- each time the 'Event' occurs.
214331--
215−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
216−-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
332+-- Note: `tagDyn d e` differs from `tag (current d) e` in the case that `e` is firing
333+-- at the same time that `d` is changing. With `tagDyn d e`, the *new* value of `d`
217334-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
218335-- will be used, since the 'Behavior' won't be updated until the end of the frame.
219336-- Additionally, this means that the output 'Event' may not be used to directly change
220337-- the input 'Dynamic', because that would mean its value depends on itself. When creating
221338-- cyclic data flows, generally `tag (current d) e` is preferred.
222−tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
223−tagPromptlyDyn = attachPromptlyDynWith const
339+tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
340+tagDyn = attachDynWith const
224341
225342-- | Attach the current value of the 'Dynamic' to the value of the
226343-- 'Event' each time it occurs.
227344--
228−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
229−attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
230−attachPromptlyDyn = attachPromptlyDynWith (,)
345+-- Note: `attachDyn d` is not the same as `attach (current d)`. See 'tagDyn' for details.
346+attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
347+attachDyn = attachDynWith (,)
231348
232349-- | Combine the current value of the 'Dynamic' with the value of the
233350-- 'Event' each time it occurs.
234351--
235−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
236−attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
237−attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
352+-- Note: `attachDynWith f d` is not the same as `attachWith f (current d)`. See 'tagDyn' for details.
353+attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
354+attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
238355
239−-- | Create a new 'Event' by combining the value at each occurence with the
240−-- current value of the 'Dynamic' value and possibly filtering if the combining
241−-- function returns 'Nothing'.
356+-- | Create a new 'Event' by combining the value at each occurence
357+-- with the current value of the 'Dynamic' value and possibly
358+-- filtering if the combining function returns 'Nothing'.
242359--
243−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
244−-- (current d)`. See 'tagPromptlyDyn' for details.
245−attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
246−attachPromptlyDynWithMaybe f d e =
360+-- Note: `attachDynWithMaybe f d` is not the same as `attachWithMaybe f (current d)`. See 'tagDyn' for details.
361+attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
362+attachDynWithMaybe f d e =
247363 let e' = attach (current d) e
248364 in fforMaybe (align e' $ updated d) $ \case
249365 This (a, b) -> f a b -- Only the tagging event is firing, so use that
@@ -254,38 +370,36 @@ attachPromptlyDynWithMaybe f d e =
254370-- Demux
255371--------------------------------------------------------------------------------
256372
257−-- | Represents a time changing value together with an 'EventSelector' that can
258−-- efficiently detect when the underlying 'Dynamic' has a particular value.
259−-- This is useful for representing data like the current selection of a long
260−-- list.
373+-- | Represents a time changing value together with an 'EventSelector'
374+-- that can efficiently detect when the underlying Dynamic has a particular value.
375+-- This is useful for representing data like the current selection of a long list.
261376--
262377-- Semantically,
263−--
264−-- > demuxed (demux d) k === fmap (== k) d
265−--
266−-- However, the when getDemuxed is used multiple times, the complexity is only
267−-- /O(log(n))/, rather than /O(n)/ for fmap.
378+-- > getDemuxed (demux d) k === mapDyn (== k) d
379+-- However, the when getDemuxed is used multiple times, the complexity is only /O(log(n))/,
380+-- rather than /O(n)/ for mapDyn.
268381data Demux t k = Demux { demuxValue :: Behavior t k
269382 , demuxSelector :: EventSelector t (Const2 k Bool)
270383 }
271384
272−-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
273−-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
385+-- | Demultiplex an input value to a 'Demux' with many outputs. At any given time, whichever output is indicated by the given 'Dynamic' will be 'True'.
274386demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
275387demux k = Demux (current k)
276− (fan $ attachWith (\k0 k1 -> if k0 == k1
388+ (fan $ attachWith (\k0 k1 -> if k0 == k1
277389 then DMap.empty
278390 else DMap.fromList [Const2 k0 :=> Identity False,
279391 Const2 k1 :=> Identity True])
280392 (current k) (updated k))
281393
282−-- | Select a particular output of the 'Demux'; this is equivalent to (but much
283−-- faster than) mapping over the original 'Dynamic' and checking whether it is
284−-- equal to the given key.
285−demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
286−demuxed d k =
394+--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
395+-- | Select a particular output of the 'Demux'; this is equivalent to (but much faster than)
396+-- mapping over the original 'Dynamic' and checking whether it is equal to the given key.
397+getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
398+getDemuxed d k = do
287399 let e = select (demuxSelector d) (Const2 k)
288− in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
400+ bb <- hold (liftM (==k) $ sample $ demuxValue d) $ fmap return e
401+ let b = pull $ join $ sample bb
402+ return $ Dynamic b e
289403
290404--------------------------------------------------------------------------------
291405-- collectDyn
@@ -293,41 +407,6 @@ demuxed d k =
293407
294408--TODO: This whole section is badly in need of cleanup
295409
296−-- | A heterogeneous list whose type and length are fixed statically. This is
297−-- reproduced from the 'HList' package due to integration issues, and because
298−-- very little other functionality from that library is needed.
299−data HList (l::[*]) where
300− HNil :: HList '[]
301− HCons :: e -> HList l -> HList (e ': l)
302−
303−infixr 2 `HCons`
304−
305−type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
306−type instance HRevApp '[] l = l
307−type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
308−
309−hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
310−hRevApp HNil l = l
311−hRevApp (HCons x l) l' = hRevApp l (HCons x l')
312−
313−hReverse :: HList l -> HList (HRevApp l '[])
314−hReverse l = hRevApp l HNil
315−
316−hBuild :: (HBuild' '[] r) => r
317−hBuild = hBuild' HNil
318−
319−class HBuild' l r where
320− hBuild' :: HList l -> r
321−
322−instance (l' ~ HRevApp l '[])
323− => HBuild' l (HList l') where
324− hBuild' = hReverse
325−
326−instance HBuild' (a ': l) r
327− => HBuild' l (a->r) where
328− hBuild' l x = hBuild' (HCons x l)
329−
330−-- | Like 'HList', but with a functor wrapping each element.
331410data FHList f l where
332411 FHNil :: FHList f '[]
333412 FHCons :: f e -> FHList f l -> FHList f (e ': l)
@@ -344,7 +423,6 @@ instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dma
344423 HTailPtr _ `gcompare` HHeadPtr = GGT
345424 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
346425
347−-- | A typed index into a typed heterogeneous list.
348426data HListPtr l a where
349427 HHeadPtr :: HListPtr (h ': t) h
350428 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
@@ -354,10 +432,8 @@ fhlistToDMap = DMap.fromList . go
354432 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
355433 go = \case
356434 FHNil -> []
357− FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
435+ FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> (HTailPtr p) :=> v) (go t)
358436
359−-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
360−-- they must be contiguous and sorted.
361437class RebuildSortedHList l where
362438 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
363439 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
@@ -381,14 +457,17 @@ instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
381457dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
382458dmapToHList = rebuildSortedHList . DMap.toList
383459
384−-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
385−-- single 'Dynamic' whose value represents the current values of all of the
386−-- input 'Dynamic's.
387−distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
388−distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
460+distributeFHListOverDyn :: forall t m l. (Reflex t, MonadHold t m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
461+distributeFHListOverDyn l = mapDyn dmapToHList =<< distributeDMapOverDyn (fhlistToDMap l)
462+{-
463+distributeFHListOverDyn l = do
464+ let ec = undefined
465+ c0 = pull $ sequenceFHList $ natMap (sample . current) l
466+ bbc <- hold c0 $ fmap constant ec
467+ let bc = pull $ sample =<< sample bbc
468+ return $ Dynamic bc ec
469+-}
389470
390−-- | Indicates that all elements in a type-level list are applications of the
391−-- same functor.
392471class AllAreFunctors (f :: a -> *) (l :: [a]) where
393472 type FunctorList f l :: [*]
394473 toFHList :: HList (FunctorList f l) -> FHList f l
@@ -398,32 +477,26 @@ instance AllAreFunctors f '[] where
398477 type FunctorList f '[] = '[]
399478 toFHList l = case l of
400479 HNil -> FHNil
401−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
402480 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
403−#endif
404481 fromFHList FHNil = HNil
405482
406483instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
407484 type FunctorList f (h ': t) = f h ': FunctorList f t
408485 toFHList l = case l of
409486 a `HCons` b -> a `FHCons` toFHList b
410−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
411487 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
412−#endif
413488 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
414489
415−-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
416−-- single 'Dynamic' whose value represents all the current values of the input's
417−-- consitutent 'Dynamic's.
418−collectDynPure :: ( RebuildSortedHList (HListElems b)
419− , IsHList a, IsHList b
420− , AllAreFunctors (Dynamic t) (HListElems b)
421− , Reflex t
422− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
423− ) => a -> Dynamic t b
424−collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
425−
426−-- | Poor man's 'Generic's for product types only.
490+collectDyn :: ( RebuildSortedHList (HListElems b)
491+ , IsHList a, IsHList b
492+ , AllAreFunctors (Dynamic t) (HListElems b)
493+ , Reflex t, MonadHold t m
494+ , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
495+ ) => a -> m (Dynamic t b)
496+collectDyn ds =
497+ mapDyn fromHList =<< distributeFHListOverDyn (toFHList $ toHList ds)
498+
499+-- Poor man's Generic
427500class IsHList a where
428501 type HListElems a :: [*]
429502 toHList :: a -> HList (HListElems a)
@@ -434,155 +507,18 @@ instance IsHList (a, b) where
434507 toHList (a, b) = hBuild a b
435508 fromHList l = case l of
436509 a `HCons` b `HCons` HNil -> (a, b)
437−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
438510 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
439−#endif
440511
441512instance IsHList (a, b, c, d) where
442513 type HListElems (a, b, c, d) = [a, b, c, d]
443514 toHList (a, b, c, d) = hBuild a b c d
444515 fromHList l = case l of
445516 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
446−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
447517 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
448−#endif
449518
450519instance IsHList (a, b, c, d, e, f) where
451520 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
452521 toHList (a, b, c, d, e, f) = hBuild a b c d e f
453522 fromHList l = case l of
454523 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
455−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
456524 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
457−#endif
458−
459−--------------------------------------------------------------------------------
460−-- Deprecated functions
461−--------------------------------------------------------------------------------
462−
463−-- | Map a function over a 'Dynamic'.
464−{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
465−mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
466−mapDyn f = return . fmap f
467−
468−-- | 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.
469−{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
470−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)
471−mapDynM f d = do
472− let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
473− eb' = fmap constant e'
474− v0 = pull $ f =<< sample (current d)
475− bb' :: Behavior t (Behavior t b) <- hold v0 eb'
476− let b' = pull $ sample =<< sample bb'
477− return $ unsafeDynamic b' e'
478−
479−-- | Flipped version of 'mapDyn'.
480−{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
481−forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
482−forDyn a = return . ffor a
483−
484−-- | Flipped version of 'mapDynM'
485−{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
486−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)
487−forDynM d f = mapDynM f d
488−
489−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
490−-- of the tuple.
491−{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
492−splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
493−splitDyn = return . splitDynPure
494−
495−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
496−{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
497−mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
498−mconcatDyn = return . mconcat
499−
500−-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
501−{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
502−distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
503−distributeDMapOverDyn = return . distributeDMapOverDynPure
504−
505−-- | Merge two 'Dynamic's into a new one using the provided function. The new
506−-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
507−-- its value.
508−{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
509−combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
510−combineDyn f a b = return $ zipDynWith f a b
511−
512−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
513−--
514−-- > do
515−-- > person <- Person `mapDyn` dynFirstName
516−-- > `apDyn` dynListName
517−-- > `apDyn` dynAge
518−-- > `apDyn` dynAddress
519−{-# 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" #-}
520−#ifdef USE_TEMPLATE_HASKELL
521−{-# ANN apDyn "HLint: ignore Use fmap" #-}
522−#endif
523−apDyn :: forall t m a b. (Reflex t, Monad m)
524− => m (Dynamic t (a -> b))
525− -> Dynamic t a
526− -> m (Dynamic t b)
527−apDyn m a = fmap (<*> a) m
528−
529−--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
530−-- | This function no longer needs to be monadic, so it has been replaced by
531−-- 'demuxed', which is pure.
532−{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
533−getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
534−getDemuxed d = return . demuxed d
535−
536−-- | This function no longer needs to be monadic, so it has been replaced by
537−-- 'distributeFHListOverDynPure', which is pure.
538−{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
539−distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
540−distributeFHListOverDyn = return . distributeFHListOverDynPure
541−
542−-- | This function no longer needs to be monadic, so it has been replaced by
543−-- 'collectDynPure', which is pure.
544−{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
545−collectDyn :: ( RebuildSortedHList (HListElems b)
546− , IsHList a, IsHList b
547− , AllAreFunctors (Dynamic t) (HListElems b)
548− , Reflex t, Monad m
549− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
550− ) => a -> m (Dynamic t b)
551−collectDyn = return . collectDynPure
552−
553−-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
554−-- semantics.
555−{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
556−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
557−tagDyn = tagPromptlyDyn
558−
559−-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
560−-- semantics.
561−{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
562−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
563−attachDyn = attachPromptlyDyn
564−
565−-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
566−-- semantics.
567−{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
568−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
569−attachDynWith = attachPromptlyDynWith
570−
571−-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
572−-- its semantics.
573−{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
574−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
575−attachDynWithMaybe = attachPromptlyDynWithMaybe
576−
577−-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
578−-- current value will always be equal to the current value's current value, and
579−-- will change whenever either the inner or the outer (or both) values change.
580−{-# DEPRECATED joinDyn "Use 'join' instead" #-}
581−joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
582−joinDyn = join
583−
584−-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
585−-- been renamed to 'uniqDyn'
586−{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
587−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
588−nubDyn = uniqDyn
src/Reflex/Dynamic/TH.hs+16−63
@@ -5,36 +5,23 @@
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE TemplateHaskell #-}
77{-# LANGUAGE TypeOperators #-}
8−#ifdef USE_REFLEX_OPTIMIZER
9−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10−#endif
11−-- | Template Haskell helper functions for building complex 'Dynamic' values.
12−module Reflex.Dynamic.TH
13− ( qDynPure
14− , unqDyn
15− , mkDynPure
16− -- * Deprecated functions
17− , qDyn
18− , mkDyn
19− ) where
8+module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
209
2110import Reflex.Dynamic
2211
23−import Control.Monad.State
12+import Language.Haskell.TH
13+import qualified Language.Haskell.TH.Syntax as TH
14+import Language.Haskell.TH.Quote
2415import Data.Data
25−import Data.Generics
26−import Data.Monoid
16+import Control.Monad.State
2717import qualified Language.Haskell.Exts as Hs
2818import qualified Language.Haskell.Meta.Syntax.Translate as Hs
29−import Language.Haskell.TH
30−import Language.Haskell.TH.Quote
31−import qualified Language.Haskell.TH.Syntax as TH
19+import Data.Monoid
20+import Data.Generics
3221
33−-- | Quote a 'Dynamic' expression. Within the quoted expression, you can use
34−-- @$(unqDyn [| x |])@ to refer to any expression @x@ of type @Dynamic t a@; the
35−-- unquoted result will be of type @a@
36−qDynPure :: Q Exp -> Q Exp
37−qDynPure qe = do
22+-- | Quote a Dynamic expression. Within the quoted expression, you can use '$(unqDyn [| x |])' to refer to any expression 'x' of type 'Dynamic t a'; the unquoted result will be of type 'a'
23+qDyn :: Q Exp -> Q Exp
24+qDyn qe = do
3825 e <- qe
3926 let f :: forall d. Data d => d -> StateT [(Name, Exp)] Q d
4027 f d = case eqT of
@@ -49,34 +36,21 @@ qDynPure qe = do
4936 let exprs = reverse exprsReversed
5037 arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
5138 param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
52− [| $(return $ LamE [param] e') <$> distributeFHListOverDynPure $(return arg) |]
39+ [| mapDyn $(return $ LamE [param] e') =<< distributeFHListOverDyn $(return arg) |]
5340
54−-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
55−-- 'qDyn' quotation.
5641unqDyn :: Q Exp -> Q Exp
5742unqDyn e = [| unqMarker $e |]
5843
59−-- | This type represents an occurrence of unqDyn before it has been processed
60−-- by qDyn. If you see it in a type error, it probably means that unqDyn has
61−-- been used outside of a qDyn context.
44+-- | This type represents an occurrence of unqDyn before it has been processed by qDyn. If you see it in a type error, it probably means that unqDyn has been used outside of a qDyn context.
6245data UnqDyn
6346
64−-- unqMarker must not be exported; it is used only as a way of smuggling data
65−-- from unqDyn to qDyn
66−
47+-- unqMarker must not be exported; it is used only as a way of smuggling data from unqDyn to qDyn
6748--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
6849unqMarker :: a -> UnqDyn
6950unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
7051
71−-- | Create a 'Dynamic' value using other 'Dynamic's as inputs. The result is
72−-- sometimes more concise and readable than the equivalent 'Applicative'-based
73−-- expression. For example:
74−--
75−-- > [mkDyn| $x + $v * $t + 1/2 * $a * $t ^ 2 |]
76−--
77−-- would have a very cumbersome 'Applicative' encoding.
78−mkDynPure :: QuasiQuoter
79−mkDynPure = QuasiQuoter
52+mkDyn :: QuasiQuoter
53+mkDyn = QuasiQuoter
8054 { quoteExp = mkDynExp
8155 , quotePat = error "mkDyn: pattern splices are not supported"
8256 , quoteType = error "mkDyn: type splices are not supported"
@@ -86,7 +60,7 @@ mkDynPure = QuasiQuoter
8660mkDynExp :: String -> Q Exp
8761mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
8862 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
89− Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
63+ Hs.ParseOk e -> qDyn $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
9064 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
9165#if MIN_VERSION_haskell_src_exts(1,18,0)
9266 antiE :: Hs.Exp Hs.SrcSpanInfo -> Hs.Exp Hs.SrcSpanInfo
@@ -107,24 +81,3 @@ mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ H
10781 reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
10882 | modName == modName' && occName == occName' = 'unqMarker
10983 reinstateUnqDyn x = x
110−
111−--------------------------------------------------------------------------------
112−-- Deprecated
113−--------------------------------------------------------------------------------
114−
115−{-# DEPRECATED qDyn "Instead of $(qDyn x), use return $(qDynPure x)" #-}
116−-- | Like 'qDynPure', but wraps its result monadically using 'return'. This is
117−-- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.
118−qDyn :: Q Exp -> Q Exp
119−qDyn qe = [| return $(qDynPure qe) |]
120−
121−{-# DEPRECATED mkDyn "Instead of [mkDyn| x |], use return [mkDynPure| x |]" #-}
122−-- | Like 'mkDynPure', but wraps its result monadically using 'return'. This is
123−-- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.
124−mkDyn :: QuasiQuoter
125−mkDyn = QuasiQuoter
126− { quoteExp = \s -> [| return $(mkDynExp s) |]
127− , quotePat = error "mkDyn: pattern splices are not supported"
128− , quoteType = error "mkDyn: type splices are not supported"
129− , quoteDec = error "mkDyn: declaration splices are not supported"
130− }
src/Reflex/Dynamic/Uniq.hsdeleted−105
@@ -1,105 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−#ifdef USE_REFLEX_OPTIMIZER
6−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
7−#endif
8−-- | This module provides a variation of 'Dynamic' values that uses cheap
9−-- pointer equality checks to reduce the amount of signal propagation needed.
10−module Reflex.Dynamic.Uniq
11− ( UniqDynamic
12− , uniqDynamic
13− , fromUniqDynamic
14− , alreadyUniqDynamic
15− ) where
16−
17−import Control.Applicative (Applicative (..))
18−import GHC.Exts
19−import Reflex.Class
20−import Reflex.Dynamic
21−
22−-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
23−-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
24−-- the value inside a 'UniqDynamic' is always evaluated to
25−-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
26−--
27−-- Internally, 'UniqDynamic' uses pointer equality as a heuristic to avoid
28−-- unnecessary update propagation; this is much more efficient than performing
29−-- full comparisons. However, when the 'UniqDynamic' is converted back into a
30−-- regular 'Dynamic', a full comparison is performed.
31−newtype UniqDynamic t a = UniqDynamic { unUniqDynamic :: Dynamic t a }
32−
33−-- | Construct a 'UniqDynamic' by eliminating redundant updates from a 'Dynamic'.
34−uniqDynamic :: Reflex t => Dynamic t a -> UniqDynamic t a
35−uniqDynamic d = UniqDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
36− old <- sample $ current d --TODO: Is it better to sample ourselves here?
37− return $ unsafeJustChanged old new
38−
39−-- | Retrieve a normal 'Dynamic' from a 'UniqDynamic'. This will perform a
40−-- final check using the output type's 'Eq' instance to ensure deterministic
41−-- behavior.
42−--
43−-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
44−-- specifically, if there are cases where @x /= x@, 'fromUniqDynamic' may
45−-- eliminate more 'updated' occurrences than it should. For example, NaN values
46−-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
47−-- instance, but can be equal by pointer equality. This may cause 'UniqDynamic'
48−-- to lose changes from NaN to NaN.
49−fromUniqDynamic :: (Reflex t, Eq a) => UniqDynamic t a -> Dynamic t a
50−fromUniqDynamic (UniqDynamic d) = uniqDynBy superEq d
51− where
52− -- Only consider values different if they fail both pointer equality /and/
53− -- 'Eq' equality. This is to make things a bit more deterministic in the
54− -- case of unlawful 'Eq' instances. However, it is still possible to
55− -- achieve nondeterminism by constructing elements that are identical in
56− -- value, unequal according to 'Eq', and nondeterministically equal or
57− -- nonequal by pointer quality. I suspect that it is impossible to make the
58− -- behavior deterministic in this case.
59− superEq a b = a `unsafePtrEq` b || a == b
60−
61−-- | Create a UniqDynamic without uniqing it on creation. This will be slightly
62−-- faster than uniqDynamic when used with a Dynamic whose values are always (or
63−-- nearly always) different from its previous values; if used with a Dynamic
64−-- whose values do not change frequently, it may be much slower than uniqDynamic
65−alreadyUniqDynamic :: Dynamic t a -> UniqDynamic t a
66−alreadyUniqDynamic = UniqDynamic
67−
68−unsafePtrEq :: a -> a -> Bool
69−unsafePtrEq a b = case a `seq` b `seq` reallyUnsafePtrEquality# a b of
70− 0# -> False
71− _ -> True
72−
73−unsafeJustChanged :: a -> a -> Maybe a
74−unsafeJustChanged old new =
75− if old `unsafePtrEq` new
76− then Nothing
77− else Just new
78−
79−instance Reflex t => Accumulator t (UniqDynamic t) where
80− accumMaybeM f z e = do
81− let f' old change = do
82− mNew <- f old change
83− return $ unsafeJustChanged old =<< mNew
84− d <- accumMaybeM f' z e
85− return $ UniqDynamic d
86− mapAccumMaybeM f z e = do
87− let f' old change = do
88− (mNew, output) <- f old change
89− return (unsafeJustChanged old =<< mNew, output)
90− (d, out) <- mapAccumMaybeM f' z e
91− return (UniqDynamic d, out)
92−
93−instance Reflex t => Functor (UniqDynamic t) where
94− fmap f (UniqDynamic d) = uniqDynamic $ fmap f d
95−
96−instance Reflex t => Applicative (UniqDynamic t) where
97− pure = UniqDynamic . constDyn
98− UniqDynamic a <*> UniqDynamic b = uniqDynamic $ a <*> b
99− _ *> b = b
100− a <* _ = a
101−
102−instance Reflex t => Monad (UniqDynamic t) where
103− UniqDynamic x >>= f = uniqDynamic $ x >>= unUniqDynamic . f
104− _ >> b = b
105− return = pure
src/Reflex/DynamicWriter.hsdeleted−193
@@ -1,193 +0,0 @@
1−-- | This module defines 'MonadDynamicWriter' and 'DynamicWriterT', its standard
2−-- implementation.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE LambdaCase #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.DynamicWriter
18− ( MonadDynamicWriter (..)
19− , DynamicWriterT (..)
20− , runDynamicWriterT
21− , withDynamicWriterT
22− ) where
23−
24−import Control.Monad.Exception
25−import Control.Monad.Identity
26−import Control.Monad.IO.Class
27−import Control.Monad.Reader
28−import Control.Monad.Ref
29−import Control.Monad.State.Strict
30−import Data.Align
31−import Data.Dependent.Map (DMap)
32−import qualified Data.Dependent.Map as DMap
33−import Data.Functor.Misc
34−import Data.Map (Map)
35−import qualified Data.Map as Map
36−import Data.Semigroup
37−import Data.Some (Some)
38−import Data.These
39−import Reflex.Class
40−import Reflex.Host.Class
41−import Reflex.PerformEvent.Class
42−import Reflex.PostBuild.Class
43−import Reflex.Requester.Class
44−import Reflex.TriggerEvent.Class
45−
46−instance MonadTrans (DynamicWriterT t w) where
47− lift = DynamicWriterT . lift
48−
49−mapIncrementalMapValues :: (Reflex t, Patch (p v), Patch (p v'), PatchTarget (p v) ~ f v, PatchTarget (p v') ~ f v', Functor p, Functor f) => (v -> v') -> Incremental t (p v) -> Incremental t (p v')
50−mapIncrementalMapValues f = unsafeMapIncremental (fmap f) (fmap f)
51−
52−mergeDynIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Dynamic t v)) -> Incremental t (PatchMap k v)
53−mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
54− where changedValues = fmap (PatchMap . fmap Just) $ mergeMapIncremental $ mapIncrementalMapValues updated a
55− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
56−
57−mergeDynIncrementalWithMove :: (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Dynamic t v)) -> Incremental t (PatchMapWithMove k v)
58−mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ alignWith f addedAndRemovedValues changedValues
59− where changedValues = mergeMapIncrementalWithMove $ mapIncrementalMapValues updated a
60− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ fmap unsafePatchMapWithMove . mapM (mapM (sample . current)) . unPatchMapWithMove
61− f x = unsafePatchMapWithMove $
62− let (p, changed) = case x of
63− This p_ -> (unPatchMapWithMove p_, mempty)
64− That c -> (mempty, c)
65− These p_ c -> (unPatchMapWithMove p_, c)
66− (pWithNewVals, noLongerMoved) = flip runState [] $ forM p $ \case
67− MapEdit_Insert moved v -> return $ MapEdit_Insert moved v
68− MapEdit_Delete moved -> return $ MapEdit_Delete moved
69− MapEdit_Move moved k -> case Map.lookup k changed of
70− Nothing -> return $ MapEdit_Move moved k
71− Just v -> do
72− modify (k:)
73− return $ MapEdit_Insert moved v
74− noLongerMovedMap = Map.fromList $ fmap (\k -> (k, ())) noLongerMoved
75− removeMovedFlag = \case --TODO: Check if moved was true before, in each case?
76− MapEdit_Insert _ v -> MapEdit_Insert False v
77− MapEdit_Delete _ -> MapEdit_Delete False
78− MapEdit_Move _ k -> MapEdit_Move False k
79− in Map.differenceWith (\e _ -> Just $ removeMovedFlag e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
80−
81−-- | A basic implementation of 'MonadDynamicWriter'.
82−newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
83−
84−instance MonadRef m => MonadRef (DynamicWriterT t w m) where
85− type Ref (DynamicWriterT t w m) = Ref m
86− newRef = lift . newRef
87− readRef = lift . readRef
88− writeRef r = lift . writeRef r
89−
90−instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
91− atomicModifyRef r = lift . atomicModifyRef r
92−
93−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
94− newEventWithTrigger = lift . newEventWithTrigger
95− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
96−
97−-- | Run a 'DynamicWriterT' action. The dynamic writer output will be provided
98−-- along with the result of the action.
99−runDynamicWriterT :: (MonadFix m, Reflex t, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
100−runDynamicWriterT (DynamicWriterT a) = do
101− (result, ws) <- runStateT a []
102− return (result, mconcat $ reverse ws)
103−
104−-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
105−-- and combines them monoidally to provide a 'Dynamic' result.
106−class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where
107− tellDyn :: Dynamic t w -> m ()
108−
109−instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
110− tellDyn w = DynamicWriterT $ modify (w :)
111−
112−instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
113− ask = lift ask
114− local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
115− reader = lift . reader
116−
117−instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
118− type Performable (DynamicWriterT t w m) = Performable m
119− performEvent_ = lift . performEvent_
120− performEvent = lift . performEvent
121−
122−instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
123− newTriggerEvent = lift newTriggerEvent
124− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
125− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
126−
127−instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
128− getPostBuild = lift getPostBuild
129−
130−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
131− tellDyn = lift . tellDyn
132−
133−instance MonadState s m => MonadState s (DynamicWriterT t w m) where
134− get = lift get
135− put = lift . put
136−
137−newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
138−
139−-- | When the execution of a 'DynamicWriterT' action is adjusted using
140−-- 'MonadAdjust', the 'Dynamic' output of that action will also be updated to
141−-- match.
142−instance (MonadAdjust t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => MonadAdjust t (DynamicWriterT t w m) where
143− runWithReplace a0 a' = do
144− (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
145− tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
146− return (fst result0, fst <$> result')
147− traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap weakenPatchDMapWith mergeDynIncremental
148− traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeDynIncrementalWithMove
149−
150−traverseDMapWithKeyWithAdjustImpl :: forall t w k v' p p' v m. (PatchTarget (p' (Some k) (Dynamic t w)) ~ Map (Some k) (Dynamic t w), PatchTarget (p' (Some k) w) ~ Map (Some k) w, Patch (p' (Some k) w), Patch (p' (Some k) (Dynamic t w)), MonadFix m, Monoid w, Reflex t, MonadHold t m)
151− => ( (forall a. k a -> v a -> m (DynamicWriterTLoweredResult t w v' a))
152− -> DMap k v
153− -> Event t (p k v)
154− -> m (DMap k (DynamicWriterTLoweredResult t w v'), Event t (p k (DynamicWriterTLoweredResult t w v')))
155− )
156− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> v' a) -> p k (DynamicWriterTLoweredResult t w v') -> p k v')
157− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> Dynamic t w) -> p k (DynamicWriterTLoweredResult t w v') -> p' (Some k) (Dynamic t w))
158− -> (Incremental t (p' (Some k) (Dynamic t w)) -> Incremental t (p' (Some k) w))
159− -> (forall a. k a -> v a -> DynamicWriterT t w m (v' a))
160− -> DMap k v
161− -> Event t (p k v)
162− -> DynamicWriterT t w m (DMap k v', Event t (p k v'))
163−traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith mergeMyDynIncremental f (dm0 :: DMap k v) dm' = do
164− (result0, result') <- lift $ base (\k v -> fmap DynamicWriterTLoweredResult $ runDynamicWriterT $ f k v) dm0 dm'
165− let getValue (DynamicWriterTLoweredResult (v, _)) = v
166− getWritten (DynamicWriterTLoweredResult (_, w)) = w
167− liftedResult0 = DMap.map getValue result0
168− liftedResult' = ffor result' $ mapPatch getValue
169− liftedWritten0 :: Map (Some k) (Dynamic t w)
170− liftedWritten0 = weakenDMapWith getWritten result0
171− liftedWritten' = ffor result' $ weakenPatchWith getWritten
172− --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
173− i <- holdIncremental liftedWritten0 liftedWritten'
174− tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
175− return (liftedResult0, liftedResult')
176−
177−-- | Map a function over the output of a 'DynamicWriterT'.
178−withDynamicWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m, MonadFix m)
179− => (w -> w')
180− -> DynamicWriterT t w m a
181− -> DynamicWriterT t w' m a
182−withDynamicWriterT f dw = do
183− (r, d) <- lift $ do
184− (r, d) <- runDynamicWriterT dw
185− let d' = fmap f d
186− return (r, d')
187− tellDyn d
188− return r
189−
190−instance Requester t m => Requester t (DynamicWriterT t w m) where
191− type Request (DynamicWriterT t w m) = Request m
192− type Response (DynamicWriterT t w m) = Response m
193− withRequesting f = DynamicWriterT $ withRequesting $ unDynamicWriterT . f
src/Reflex/EventWriter.hsdeleted−170
@@ -1,170 +0,0 @@
1−-- | This module provides 'EventWriterT', the standard implementation of
2−-- 'EventWriter'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16−module Reflex.EventWriter
17− ( EventWriterT (..)
18− , runEventWriterT
19− , EventWriter (..)
20− , runWithReplaceEventWriterTWith
21− , sequenceDMapWithAdjustEventWriterTWith
22− ) where
23−
24−import Reflex.Class
25−import Reflex.Host.Class
26−import Reflex.PerformEvent.Class
27−import Reflex.PostBuild.Class
28−import Reflex.Requester.Class
29−import Reflex.TriggerEvent.Class
30−
31−import Control.Monad.Exception
32−import Control.Monad.Identity
33−import Control.Monad.Reader
34−import Control.Monad.Ref
35−import Control.Monad.State.Strict
36−import Data.Dependent.Map (DMap)
37−import qualified Data.Dependent.Map as DMap
38−import Data.Foldable
39−import Data.Functor.Compose
40−import Data.Functor.Misc
41−import qualified Data.List.NonEmpty as NonEmpty
42−import Data.Map (Map)
43−import Data.Semigroup
44−import Data.Sequence
45−import Data.Some (Some)
46−import Data.Tuple
47−
48−-- | 'EventWriter' efficiently collects 'Event' values using 'tellEvent'
49−-- and combines them monoidally to provide an 'Event' result.
50−class (Monad m, Monoid w) => EventWriter t w m | m -> t w where
51− tellEvent :: Event t w -> m ()
52−
53−-- | A basic implementation of 'EventWriter'.
54−newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (Seq (Event t w)) m a }
55− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
56−
57−-- | Run a 'EventWriterT' action.
58−runEventWriterT :: (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
59−runEventWriterT (EventWriterT a) = do
60− (result, requests) <- runStateT a mempty
61− return (result, mconcat $ toList requests)
62−
63−instance (Reflex t, Monad m, Monoid w) => EventWriter t w (EventWriterT t w m) where
64− tellEvent w = EventWriterT $ modify (|> w)
65−
66−instance EventWriter t w m => EventWriter t w (ReaderT r m) where
67− tellEvent = lift . tellEvent
68−
69−instance MonadTrans (EventWriterT t w) where
70− lift = EventWriterT . lift
71−
72−instance MonadSample t m => MonadSample t (EventWriterT t w m) where
73− sample = lift . sample
74−
75−instance MonadHold t m => MonadHold t (EventWriterT t w m) where
76− hold v0 = lift . hold v0
77− holdDyn v0 = lift . holdDyn v0
78− holdIncremental v0 = lift . holdIncremental v0
79−
80−instance (Reflex t, MonadAdjust t m, MonadHold t m, Monoid w, Semigroup w) => MonadAdjust t (EventWriterT t w m) where
81− runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
82− traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
83− traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove
84−
85−instance Requester t m => Requester t (EventWriterT t w m) where
86− type Request (EventWriterT t w m) = Request m
87− type Response (EventWriterT t w m) = Response m
88− withRequesting f = EventWriterT $ withRequesting $ unEventWriterT . f
89−
90−
91−-- | Given a function like 'runWithReplace' for the underlying monad, implement
92−-- 'runWithReplace' for 'EventWriterT'. This is necessary when the underlying
93−-- monad doesn't have a 'MonadAdjust' instance or to override the default
94−-- 'MonadAdjust' behavior.
95−runWithReplaceEventWriterTWith :: forall m t w a b. (Reflex t, MonadHold t m, Monoid w)
96− => (forall a' b'. m a' -> Event t (m b') -> EventWriterT t w m (a', Event t b'))
97− -> EventWriterT t w m a
98− -> Event t (EventWriterT t w m b)
99− -> EventWriterT t w m (a, Event t b)
100−runWithReplaceEventWriterTWith f a0 a' = do
101− let g :: EventWriterT t w m c -> m (c, Seq (Event t w))
102− g (EventWriterT r) = runStateT r mempty
103− (result0, result') <- f (g a0) $ fmap g a'
104− request <- holdDyn (fmapCheap (mconcat . NonEmpty.toList) $ mergeList $ toList $ snd result0) $ fmapCheap (fmapCheap (mconcat . NonEmpty.toList) . mergeList . toList . snd) result'
105− -- We add these two separately to take advantage of the free merge being done later. The coincidence case must come first so that it has precedence if both fire simultaneously. (Really, we should probably block the 'switch' whenever 'updated' fires, but switchPromptlyDyn has the same issue.)
106− EventWriterT $ modify $ flip (|>) $ coincidence $ updated request
107− EventWriterT $ modify $ flip (|>) $ switch $ current request
108− return (fst result0, fmapCheap fst result')
109−
110−-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
111−sequenceDMapWithAdjustEventWriterTWith :: forall t m p p' w k v v'. (Reflex t, MonadHold t m, Monoid w, Semigroup w, Patch (p' (Some k) (Event t w)), PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w))
112− => ( (forall a. k a -> v a -> m (Compose ((,) (Seq (Event t w))) v' a))
113− -> DMap k v
114− -> Event t (p k v)
115− -> EventWriterT t w m (DMap k (Compose ((,) (Seq (Event t w))) v'), Event t (p k (Compose ((,) (Seq (Event t w))) v')))
116− )
117− -> ((forall a. Compose ((,) (Seq (Event t w))) v' a -> v' a) -> p k (Compose ((,) (Seq (Event t w))) v') -> p k v')
118− -> ((forall a. Compose ((,) (Seq (Event t w))) v' a -> Event t w) -> p k (Compose ((,) (Seq (Event t w))) v') -> p' (Some k) (Event t w))
119− -> (p' (Some k) (Event t w) -> [Event t w])
120− -> (Incremental t (p' (Some k) (Event t w)) -> Event t (Map (Some k) w))
121− -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
122− -> DMap k v
123− -> Event t (p k v)
124− -> EventWriterT t w m (DMap k v', Event t (p k v'))
125−sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
126− let f' :: forall a. k a -> v a -> m (Compose ((,) (Seq (Event t w))) v' a)
127− f' k v = Compose . swap <$> runStateT (unEventWriterT $ f k v) mempty
128− (children0, children') <- base f' dm0 dm'
129− let result0 = DMap.map (snd . getCompose) children0
130− result' = fforCheap children' $ mapPatch $ snd . getCompose
131− requests0 :: Map (Some k) (Event t w)
132− requests0 = weakenDMapWith (mconcat . toList . fst . getCompose) children0
133− requests' :: Event t (p' (Some k) (Event t w))
134− requests' = fforCheap children' $ weakenPatchWith $ mconcat . toList . fst . getCompose
135− childRequestMap :: Incremental t (p' (Some k) (Event t w)) <- holdIncremental requests0 requests'
136− -- We add these two separately to take advantage of the free merge being done later. The coincidence case must come first so that it has precedence if both fire simultaneously. (Really, we should probably block the 'switch' whenever 'updated' fires, but switchPromptlyDyn has the same issue.)
137− EventWriterT $ modify $ flip (|>) $ coincidence $ fforCheap requests' $ \p -> mconcat $ patchNewElements p --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
138− EventWriterT $ modify $ flip (|>) $ fold <$> mergePatchIncremental childRequestMap
139− return (result0, result')
140−
141−instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
142− type Performable (EventWriterT t w m) = Performable m
143− performEvent_ = lift . performEvent_
144− performEvent = lift . performEvent
145−
146−instance PostBuild t m => PostBuild t (EventWriterT t w m) where
147− getPostBuild = lift getPostBuild
148−
149−instance TriggerEvent t m => TriggerEvent t (EventWriterT t w m) where
150− newTriggerEvent = lift newTriggerEvent
151− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
152− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
153−
154−instance MonadReader r m => MonadReader r (EventWriterT t w m) where
155− ask = lift ask
156− local f (EventWriterT a) = EventWriterT $ mapStateT (local f) a
157− reader = lift . reader
158−
159−instance MonadRef m => MonadRef (EventWriterT t w m) where
160− type Ref (EventWriterT t w m) = Ref m
161− newRef = lift . newRef
162− readRef = lift . readRef
163− writeRef r = lift . writeRef r
164−
165−instance MonadAtomicRef m => MonadAtomicRef (EventWriterT t w m) where
166− atomicModifyRef r = lift . atomicModifyRef r
167−
168−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (EventWriterT t w m) where
169− newEventWithTrigger = lift . newEventWithTrigger
170− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
src/Reflex/FunctorMaybe.hsdeleted−29
@@ -1,29 +0,0 @@
1−-- | This module defines the FunctorMaybe class, which extends Functors with the
2−-- ability to delete values.
3−module Reflex.FunctorMaybe
4− ( FunctorMaybe (..)
5− ) where
6−
7−import Data.Maybe
8−
9−--TODO: See if there's a better class in the standard libraries already
10−
11−-- | A class for values that combines filtering and mapping using 'Maybe'.
12−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
13−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
14−-- and e.g. 'Event' is instance of neither.
15−--
16−-- A definition of 'fmapMaybe' must satisfy the following laws:
17−--
18−-- [/identity/]
19−-- @'fmapMaybe' 'Just' ≡ 'id'@
20−--
21−-- [/composition/]
22−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
23−class FunctorMaybe f where
24− -- | Combined mapping and filtering function.
25− fmapMaybe :: (a -> Maybe b) -> f a -> f b
26−
27−-- | @fmapMaybe f = catMaybes . fmap f@
28−instance FunctorMaybe [] where
29− fmapMaybe f = catMaybes . fmap f
src/Reflex/Host/Class.hs+55−127
@@ -1,32 +1,5 @@
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
1+{-# LANGUAGE ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, FunctionalDependencies, FlexibleContexts #-}
2+module Reflex.Host.Class where
303
314import Reflex.Class
325
@@ -34,76 +7,49 @@ import Control.Applicative
347import Control.Monad
358import Control.Monad.Fix
369import Control.Monad.Identity
37−import Control.Monad.Ref
3810import 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 ())
11+import Control.Monad.Trans.Reader (ReaderT())
12+import Control.Monad.Trans.Writer (WriterT())
13+import Control.Monad.Trans.Cont (ContT())
14+import Control.Monad.Trans.Except (ExceptT())
15+import Control.Monad.Trans.RWS (RWST())
16+import Control.Monad.Trans.State (StateT())
4417import qualified Control.Monad.Trans.State.Strict as Strict
45−import Control.Monad.Trans.Writer (WriterT ())
4618import Data.Dependent.Sum (DSum (..))
47−import Data.GADT.Compare
4819import 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
20+import Data.GADT.Compare
21+import Control.Monad.Ref
5822
5923-- 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
24+import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
25+
26+-- | Framework implementation support class for the reflex implementation represented by @t@.
27+class (Reflex t, MonadReflexCreateTrigger t (HostFrame t), MonadSample t (HostFrame t), MonadHold t (HostFrame t), MonadFix (HostFrame t), MonadSubscribeEvent t (HostFrame t)) => ReflexHost t where
7528 type EventTrigger t :: * -> *
7629 type EventHandle t :: * -> *
7730 type HostFrame t :: * -> *
7831
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
32+-- | Monad in which Events can be 'subscribed'. This forces all underlying event sources to be initialized, so that the event will fire whenever it ought to. Events must be subscribed before they are read using readEvent
8233class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
8334 -- | Subscribe to an event and set it up if needed.
8435 --
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'.
36+ -- This function will create a new 'EventHandle' from an 'Event'. This handle may then be used via
37+ -- 'readEvent' in the read callback of 'fireEventsAndRead'.
8838 --
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.
39+ -- If the event wasn't subscribed to before (either manually or through a dependent event or behavior)
40+ -- then this function will cause the event and all dependencies of this event to be set up.
41+ -- For example, if the event was created by 'newEventWithTrigger', then it's callback will be executed.
9342 --
9443 -- It's safe to call this function multiple times.
9544 subscribeEvent :: Event t a -> m (EventHandle t a)
9645
9746-- | Monad that allows to read events' values.
9847class (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').
48+ -- | Read the value of an 'Event' from an 'EventHandle' (created by calling 'subscribeEvent').
10149 --
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@.
50+ -- After event propagation is done, all events can be in two states: either they are firing with some value or they are not firing.
51+ -- In the former case, this function returns @Just act@, where @act@ in an action to read the current value of
52+ -- the event. In the latter case, the function returns @Nothing@.
10753 --
10854 -- This function is normally used in the calllback for 'fireEventsAndRead'.
10955 readEvent :: EventHandle t a -> m (Maybe (m a))
@@ -112,54 +58,42 @@ class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t wher
11258class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
11359 -- | Creates a root 'Event' (one that is not based on any other event).
11460 --
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.
61+ -- When a subscriber first subscribes to an event (building another event
62+ -- that depends on the subscription) the given callback function is run and
63+ -- passed a trigger. The callback function can then set up the event source in IO.
64+ -- After this is done, the callback function must return an accompanying teardown action.
12065 --
121− -- Any time between setup and teardown the trigger can be used to fire the
122− -- event, by passing it to 'fireEventsAndRead'.
66+ -- Any time between setup and teardown the trigger can be used to fire
67+ -- the event, by passing it to 'fireEventsAndRead'.
12368 --
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.
69+ -- Note: An event may be set up multiple times. So after the teardown action is executed, the event may still be set up again in the future.
12670 newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
12771 newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
12872
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
73+class (ReflexHost t, MonadReflexCreateTrigger t m, MonadSubscribeEvent t m, MonadReadEvent t (ReadPhase m), MonadSample t (ReadPhase m), MonadHold t (ReadPhase m)) => MonadReflexHost t m | m -> t where
13774 type ReadPhase m :: * -> *
13875 -- | Propagate some events firings and read the values of events afterwards.
13976 --
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.
77+ -- This function will create a new frame to fire the given events. It will then update all
78+ -- dependent events and behaviors. After that is done, the given callback is executed which
79+ -- allows to read the final values of events and check whether they have fired in this frame or
80+ -- not.
14481 --
14582 -- All events that are given are fired at the same time.
14683 --
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
84+ -- This function is typically used in the main loop of a reflex framework implementation.
85+ -- The main loop waits for external events to happen (such as keyboard input or a mouse click)
86+ -- and then fires the corresponding events using this function. The read callback can be used
87+ -- to read output events and perform a corresponding response action to the external event.
88+ fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> (ReadPhase m a) -> m a
15389
15490 -- | Run a frame without any events firing.
15591 --
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.
92+ -- This function should be used when you want to use 'sample' and 'hold' when no events are currently firing.
93+ -- Using this function in that case can improve performance, since the implementation can assume that no events
94+ -- are firing when 'sample' or 'hold' are called.
16095 --
161− -- This function is commonly used to set up the basic event network when the
162− -- application starts up.
96+ -- This function is commonly used to set up the basic event network when the application starts up.
16397 runHostFrame :: HostFrame t a -> m a
16498
16599-- | Like 'fireEventsAndRead', but without reading any events.
@@ -169,13 +103,10 @@ fireEvents dm = fireEventsAndRead dm $ return ()
169103
170104-- | Create a new event and store its trigger in an 'IORef' while it's active.
171105--
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.
106+-- An event is only active between the set up (when it's first subscribed to) and the teardown phases (when noboby is subscribing the event anymore).
107+-- This function returns an Event and an 'IORef'. As long as the event is active, the 'IORef' will contain 'Just' the event trigger to
108+-- trigger this event. When the event is not active, the 'IORef' will contain 'Nothing'. This allows event sources to be more efficient,
109+-- since they don't need to produce events when nobody is listening.
179110newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
180111newEventWithTriggerRef = do
181112 rt <- newRef Nothing
@@ -185,7 +116,6 @@ newEventWithTriggerRef = do
185116 return (e, rt)
186117{-# INLINE newEventWithTriggerRef #-}
187118
188−-- | Fire the given trigger if it is not 'Nothing'.
189119fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
190120fireEventRef mtRef input = do
191121 mt <- readRef mtRef
@@ -193,8 +123,6 @@ fireEventRef mtRef input = do
193123 Nothing -> return ()
194124 Just trigger -> fireEvents [trigger :=> Identity input]
195125
196−-- | Fire the given trigger if it is not 'Nothing', and read from the given
197−-- 'EventHandle'.
198126fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
199127fireEventRefAndRead mtRef input e = do
200128 mt <- readRef mtRef
@@ -204,7 +132,7 @@ fireEventRefAndRead mtRef input e = do
204132 mGetValue <- readEvent e
205133 case mGetValue of
206134 Nothing -> return Nothing
207− Just getValue -> fmap Just getValue
135+ Just getValue -> liftM Just getValue
208136
209137
210138--------------------------------------------------------------------------------
@@ -246,8 +174,8 @@ instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
246174 type ReadPhase (StateT s m) = ReadPhase m
247175 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
248176 runHostFrame = lift . runHostFrame
249−
250−
177+
178+
251179instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
252180 newEventWithTrigger = lift . newEventWithTrigger
253181 newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
@@ -258,9 +186,9 @@ instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) wh
258186instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
259187 type ReadPhase (Strict.StateT s m) = ReadPhase m
260188 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
261− runHostFrame = lift . runHostFrame
262−
263−
189+ runHostFrame = lift . runHostFrame
190+
191+
264192
265193instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
266194 newEventWithTrigger = lift . newEventWithTrigger
src/Reflex/Optimizer.hsdeleted−51
@@ -1,51 +0,0 @@
1−-- | This module provides a GHC plugin designed to improve code that uses
2−-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level
3−-- definition that doesn't have an explicit inlining pragma. In the future,
4−-- additional optimizations are likely to be added.
5−{-# LANGUAGE CPP #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE OverloadedStrings #-}
8−module Reflex.Optimizer
9− ( plugin
10− ) where
11−
12−#ifdef ghcjs_HOST_OS
13−import Plugins
14−#else
15−import Control.Arrow
16−import CoreMonad
17−import Data.String
18−import GhcPlugins
19−#endif
20−
21−#ifdef ghcjs_HOST_OS
22−
23−-- | The GHCJS build of Reflex.Optimizer just throws an error; instead, the version built with GHC should be used.
24−plugin :: Plugin
25−plugin = error "The GHCJS build of Reflex.Optimizer cannot be used. Instead, build with GHC and use the result with GHCJS."
26−
27−#else
28−
29−-- | The GHC plugin itself. See "GhcPlugins" for more details.
30−plugin :: Plugin
31−plugin = defaultPlugin { installCoreToDos = install }
32−
33−install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
34−install [] p = return $ makeInlinable : p
35−install options@(_:_) p = do
36− warnMsg $ "Reflex.Optimizer: ignoring " <> fromString (show $ length options) <> " command-line options"
37− install [] p
38−
39−makeInlinable :: CoreToDo
40−makeInlinable = CoreDoPluginPass "MakeInlinable" $ \modGuts -> do
41− let f v = setIdInfo v $ let i = idInfo v in
42− setInlinePragInfo i $ let p = inlinePragInfo i in
43− if isDefaultInlinePragma p
44− then defaultInlinePragma { inl_inline = Inlinable }
45− else p
46− newBinds = flip map (mg_binds modGuts) $ \case
47− NonRec b e -> NonRec (f b) e
48− Rec bes -> Rec $ map (first f) bes
49− return $ modGuts { mg_binds = newBinds }
50−
51−#endif
src/Reflex/Patch.hsdeleted−387
@@ -1,387 +0,0 @@
1−-- | This module defines the 'Patch' class, which is used by Reflex to manage
2−-- changes to 'Reflex.Class.Incremental' values.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DeriveFoldable #-}
5−{-# LANGUAGE DeriveFunctor #-}
6−{-# LANGUAGE DeriveTraversable #-}
7−{-# LANGUAGE FlexibleContexts #-}
8−{-# LANGUAGE GADTs #-}
9−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10−{-# LANGUAGE LambdaCase #-}
11−{-# LANGUAGE PolyKinds #-}
12−{-# LANGUAGE Rank2Types #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE StandaloneDeriving #-}
15−{-# LANGUAGE TypeFamilies #-}
16−module Reflex.Patch
17− ( Patch (..)
18− , PatchDMap (..)
19− , mapPatchDMap
20− , traversePatchDMap
21− , traversePatchDMapWithKey
22− , weakenPatchDMapWith
23− , patchDMapToPatchMapWith
24− , const2PatchDMapWith
25− , ComposeMaybe (..)
26− , PatchMap (..)
27− , patchMapNewElements
28− , Group (..)
29− , Additive
30− , AdditivePatch (..)
31− , PatchMapWithMove
32− , unPatchMapWithMove
33− , unsafePatchMapWithMove
34− , patchMapWithMoveNewElements
35− , MapEdit (..)
36− , mapEditMoved
37− , mapEditSetMoved
38− , PatchDMapWithMove
39− , unPatchDMapWithMove
40− , unsafePatchDMapWithMove
41− , patchThatSortsMapWith
42− , patchThatChangesAndSortsMapWith
43− , mapPatchDMapWithMove
44− , traversePatchDMapWithMove
45− , traversePatchDMapWithMoveWithKey
46− , weakenPatchDMapWithMoveWith
47− , patchDMapWithMoveToPatchMapWithMoveWith
48− , const2PatchDMapWithMoveWith
49− , DMapEdit (..)
50− , dmapEditMoved
51− -- * Test functions
52− , validPatchMapWithMove
53− , knownValidPatchMapWithMove
54− , knownInvalidPatchMapWithMove
55− ) where
56−
57−import Control.Arrow
58−import Control.Monad.Identity
59−import Control.Monad.State
60−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
61−import qualified Data.Dependent.Map as DMap
62−import Data.Foldable
63−import Data.Function
64−import Data.Functor.Constant
65−import Data.Functor.Misc
66−import Data.List
67−import Data.Map (Map)
68−import qualified Data.Map as Map
69−import Data.Maybe
70−import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid, (<>))
71−import qualified Data.Set as Set
72−import Data.Some (Some)
73−import qualified Data.Some as Some
74−import Data.Tuple
75−
76−-- | A 'Patch' type represents a kind of change made to a datastructure.
77−class Patch p where
78− type PatchTarget p :: *
79− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
80− -- 'Nothing'.
81− apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
82−
83−-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
84−-- make the 'f' parameter have a nominal type role. We need f to be
85−-- representational so that we can use safe 'coerce'.
86−newtype ComposeMaybe f a = ComposeMaybe { getComposeMaybe :: Maybe (f a) }
87−
88−deriving instance Functor f => Functor (ComposeMaybe f)
89−
90−instance Patch (Identity a) where
91− type PatchTarget (Identity a) = a
92− apply (Identity a) _ = Just a
93−
94−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
95−-- deleted.
96−newtype PatchDMap k v = PatchDMap (DMap k (ComposeMaybe v))
97−
98−deriving instance GCompare k => Semigroup (PatchDMap k v)
99−
100−deriving instance GCompare k => Monoid (PatchDMap k v)
101−
102−instance GCompare k => Patch (PatchDMap k v) where
103− type PatchTarget (PatchDMap k v) = DMap k v
104− 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?
105− where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
106− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
107− nothingToJust = \case
108− Nothing -> Just $ Constant ()
109− Just _ -> Nothing
110−
111−mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
112−mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
113−
114−traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
115−traversePatchDMap f = traversePatchDMapWithKey $ const f
116−
117−traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
118−traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
119−
120−weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
121−weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
122−
123−patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
124−patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
125−
126−const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
127−const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
128− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
129− g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
130−
131−-- | A set of changes to a 'Map'. Any element may be inserted/updated or
132−-- deleted.
133−newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
134−
135−instance Ord k => Patch (PatchMap k v) where
136− type PatchTarget (PatchMap k v) = Map k v
137− 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?
138− where insertions = Map.mapMaybeWithKey (const id) p
139− deletions = Map.mapMaybeWithKey (const nothingToJust) p
140− nothingToJust = \case
141− Nothing -> Just ()
142− Just _ -> Nothing
143−
144−instance Ord k => Semigroup (PatchMap k v) where
145− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
146− -- PatchMap is idempotent, so stimes n is id for every n
147−#if MIN_VERSION_semigroups(0,17,0)
148− stimes = stimesIdempotentMonoid
149−#else
150− times1p n x = case compare n 0 of
151− LT -> error "stimesIdempotentMonoid: negative multiplier"
152− EQ -> mempty
153− GT -> x
154−#endif
155−
156−instance Ord k => Monoid (PatchMap k v) where
157− mempty = PatchMap mempty
158− mappend = (<>)
159−
160−instance Functor (PatchMap k) where
161− fmap f = PatchMap . fmap (fmap f) . unPatchMap
162−
163−-- | Returns all the new elements that will be added to the 'Map'
164−patchMapNewElements :: PatchMap k v -> [v]
165−patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
166−
167−---- Patches based on commutative groups
168−
169−-- | A 'Group' is a 'Monoid' where every element has an inverse.
170−class (Semigroup q, Monoid q) => Group q where
171− negateG :: q -> q
172− (~~) :: q -> q -> q
173− r ~~ s = r <> negateG s
174−
175−-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
176−class Semigroup q => Additive q where
177−
178−-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
179−newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
180−
181−instance Additive p => Patch (AdditivePatch p) where
182− type PatchTarget (AdditivePatch p) = p
183− apply (AdditivePatch p) q = Just $ p <> q
184−
185−data MapEdit k v
186− = MapEdit_Insert Bool v -- ^ Insert the given value here
187− | MapEdit_Delete Bool -- ^ Delete the existing value, if any, from here
188− | MapEdit_Move Bool k -- ^ Move the value here from the given key
189− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
190−
191−mapEditMoved :: MapEdit k v -> Bool
192−mapEditMoved = \case
193− MapEdit_Insert moved _ -> moved
194− MapEdit_Delete moved -> moved
195− MapEdit_Move moved _ -> moved
196−
197−mapEditSetMoved :: Bool -> MapEdit k v -> MapEdit k v
198−mapEditSetMoved moved = \case
199− MapEdit_Insert _ v -> MapEdit_Insert moved v
200− MapEdit_Delete _ -> MapEdit_Delete moved
201− MapEdit_Move _ k -> MapEdit_Move moved k
202−
203−-- | Patch a DMap with additions, deletions, and moves.
204−-- Invariants:
205−-- * The same source key may not be moved to two destination keys.
206−-- * Any key that is the source of a Move must also be edited
207−-- (otherwise, the Move would duplicate it)
208−data PatchMapWithMove k v = PatchMapWithMove (Map k (MapEdit k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
209−
210−unPatchMapWithMove :: PatchMapWithMove k v -> Map k (MapEdit k v)
211−unPatchMapWithMove (PatchMapWithMove p) = p
212−
213−-- | Warning: when using this function, you must ensure that the invariants of
214−-- 'PatchMapWithMove' are preserved; they will not be checked.
215−unsafePatchMapWithMove :: Map k (MapEdit k v) -> PatchMapWithMove k v
216−unsafePatchMapWithMove = PatchMapWithMove
217−
218−instance Ord k => Patch (PatchMapWithMove k v) where
219− type PatchTarget (PatchMapWithMove k v) = Map k v
220− apply (PatchMapWithMove p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
221− where insertions = flip Map.mapMaybeWithKey p $ const $ \case
222− MapEdit_Insert _ v -> Just v
223− MapEdit_Move _ k -> Map.lookup k old
224− MapEdit_Delete _ -> Nothing
225− deletions = flip Map.mapMaybeWithKey p $ const $ \case
226− MapEdit_Delete _ -> Just ()
227− _ -> Nothing
228−
229−-- | Returns all the new elements that will be added to the 'Map'
230−patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
231−patchMapWithMoveNewElements (PatchMapWithMove p) = catMaybes $ flip fmap (Map.elems p) $ \case
232− MapEdit_Insert _ v -> Just v
233− MapEdit_Move _ _ -> Nothing
234− MapEdit_Delete _ -> Nothing
235−
236−validPatchMapWithMove :: Ord k => PatchMapWithMove k v -> Bool
237−validPatchMapWithMove (PatchMapWithMove p) =
238− let moveSources = catMaybes $ flip fmap (Map.elems p) $ \case
239− MapEdit_Move _ k -> Just k
240− _ -> Nothing
241− in and
242− [ -- All Moves must have unique source keys - a single source key can't be moved to two destinations
243− all (==1) $ Map.fromListWith (+) $ fmap (\k -> (k, 1 :: Integer )) moveSources
244− , -- All Move source keys must also be patched - since the value's being moved elsewhere, the original must either be Deleted or replaced (with Insert or Move)
245− all (isJust . flip Map.lookup p) moveSources
246− ]
247−
248−knownValidPatchMapWithMove :: [PatchMapWithMove Int Int]
249−knownValidPatchMapWithMove =
250− [ PatchMapWithMove mempty
251− , PatchMapWithMove $ Map.fromList
252− [ (1, MapEdit_Move True 2)
253− , (2, MapEdit_Move True 1)
254− ]
255− ]
256−
257−knownInvalidPatchMapWithMove :: [PatchMapWithMove Int Int]
258−knownInvalidPatchMapWithMove =
259− [ -- Invalid because we move 2 to 1, but don't specify what to do with 2
260− PatchMapWithMove $ Map.singleton 1 $ MapEdit_Move False 2
261− , -- Invalid because we have multiple moves from 3
262− PatchMapWithMove $ Map.fromList
263− [ (1, MapEdit_Move False 3)
264− , (2, MapEdit_Move False 3)
265− , (3, MapEdit_Delete True)
266− ]
267− ]
268−
269−patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
270−patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g l $ sortBy (cmp `on` snd) l
271− where l = Map.toList m
272− g (to, _) (from, _) = if to == from
273− then Nothing
274− else Just (to, MapEdit_Move True from)
275−
276−patchThatChangesAndSortsMapWith :: (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
277−patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patch
278− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
279− newList = Map.toList newByIndexUnsorted
280− newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
281− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
282− let f k v = do
283− remainingValues <- get
284− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
285− then Map.delete v remainingValues
286− else Map.insert v remainingKeys remainingValues
287− case Map.lookup v remainingValues of
288− Nothing -> return $ MapEdit_Insert True v -- There's no existing value we can take
289− Just fromKs ->
290− if k `Set.member` fromKs
291− then do
292− putRemainingKeys $ Set.delete k fromKs
293− return $ MapEdit_Move True k -- There's an existing value, and it's here, so no patch necessary
294− else do
295− Just (fromK, remainingKeys) <- return $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
296− putRemainingKeys remainingKeys
297− return $ MapEdit_Move True fromK
298− Map.traverseWithKey f newByIndex
299− unusedOldKeys = fold unusedValuesByValue
300− pointlessMove k = \case
301− MapEdit_Move _ k' | k == k' -> True
302− _ -> False
303− keyWasMoved k = k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
304− patch = unsafePatchMapWithMove $ Map.filterWithKey (\k v -> not $ pointlessMove k v) $ Map.mergeWithKey (\k a _ -> Just $ mapEditSetMoved (keyWasMoved k) a) (Map.mapWithKey $ \k -> mapEditSetMoved $ keyWasMoved k) (Map.mapWithKey $ \k _ -> MapEdit_Delete $ keyWasMoved k) insertsAndMoves oldByIndex
305−
306−data DMapEdit (k :: a -> *) (v :: a -> *) :: a -> * where
307− -- | Insert the given value here
308− DMapEdit_Insert :: Bool -> v a -> DMapEdit k v a
309− -- | Delete the existing value, if any, from here
310− DMapEdit_Delete :: Bool -> DMapEdit k v a
311− -- | Move the value here from the given key
312− DMapEdit_Move :: Bool -> k a -> DMapEdit k v a
313−
314−dmapEditMoved :: DMapEdit k v a -> Bool
315−dmapEditMoved = \case
316− DMapEdit_Insert moved _ -> moved
317− DMapEdit_Delete moved -> moved
318− DMapEdit_Move moved _ -> moved
319−
320−-- | Like 'PatchMapWithMove', but for 'DMap'.
321−newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (DMapEdit k v))
322−
323−unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (DMapEdit k v)
324−unPatchDMapWithMove (PatchDMapWithMove p) = p
325−
326−-- | Warning: when using this function, you must ensure that the invariants of
327−-- 'PatchDMapWithMove' are preserved; they will not be checked.
328−unsafePatchDMapWithMove :: DMap k (DMapEdit k v) -> PatchDMapWithMove k v
329−unsafePatchDMapWithMove = PatchDMapWithMove
330−
331−mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
332−mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $ DMap.map g p
333− where g :: forall a. DMapEdit k v a -> DMapEdit k v' a
334− g = \case
335− DMapEdit_Insert moved v -> DMapEdit_Insert moved $ f v
336− DMapEdit_Delete moved -> DMapEdit_Delete moved
337− DMapEdit_Move moved k -> DMapEdit_Move moved k
338−
339−traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
340−traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
341−
342−traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
343−traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey g p
344− where g :: forall a. k a -> DMapEdit k v a -> m (DMapEdit k v' a)
345− g k = \case
346− DMapEdit_Insert moved v -> DMapEdit_Insert moved <$> f k v
347− DMapEdit_Delete moved -> pure $ DMapEdit_Delete moved
348− DMapEdit_Move moved fromKey -> pure $ DMapEdit_Move moved fromKey
349−
350−weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
351−weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
352− where g :: forall a. DMapEdit k v a -> MapEdit (Some k) v'
353− g = \case
354− DMapEdit_Insert moved v -> MapEdit_Insert moved $ f v
355− DMapEdit_Delete moved -> MapEdit_Delete moved
356− DMapEdit_Move moved k -> MapEdit_Move moved $ Some.This k
357−
358−patchDMapWithMoveToPatchMapWithMoveWith :: forall k f v v'. (f v -> v') -> PatchDMapWithMove (Const2 k v) f -> PatchMapWithMove k v'
359−patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
360− where g :: DMapEdit (Const2 k v) f v -> MapEdit k v'
361− g = \case
362− DMapEdit_Insert moved v -> MapEdit_Insert moved $ f v
363− DMapEdit_Delete moved -> MapEdit_Delete moved
364− DMapEdit_Move moved (Const2 k) -> MapEdit_Move moved k
365−
366−const2PatchDMapWithMoveWith :: forall k v f a. (v -> f a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) f
367−const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
368− where g :: (k, MapEdit k v) -> DSum (Const2 k a) (DMapEdit (Const2 k a) f)
369− g (k, e) = Const2 k :=> case e of
370− MapEdit_Insert moved v -> DMapEdit_Insert moved $ f v
371− MapEdit_Delete moved -> DMapEdit_Delete moved
372− MapEdit_Move moved fromKey -> DMapEdit_Move moved $ Const2 fromKey
373−
374−instance GCompare k => Patch (PatchDMapWithMove k v) where
375− type PatchTarget (PatchDMapWithMove k v) = DMap k v
376− 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?
377− where insertions = DMap.mapMaybeWithKey insertFunc p
378− insertFunc :: forall a. k a -> DMapEdit k v a -> Maybe (v a)
379− insertFunc _ = \case
380− DMapEdit_Insert _ v -> Just v
381− DMapEdit_Move _ k -> DMap.lookup k old
382− DMapEdit_Delete _ -> Nothing
383− deletions = DMap.mapMaybeWithKey deleteFunc p
384− deleteFunc :: forall a. k a -> DMapEdit k v a -> Maybe (Constant () a)
385− deleteFunc _ = \case
386− DMapEdit_Delete _ -> Just $ Constant ()
387− _ -> Nothing
src/Reflex/PerformEvent/Base.hsdeleted−157
@@ -1,157 +0,0 @@
1−-- | This module provides 'PerformEventT', the standard implementation of
2−-- 'PerformEvent'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE PartialTypeSignatures #-}
11−{-# LANGUAGE PolyKinds #-}
12−{-# LANGUAGE RankNTypes #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE StandaloneDeriving #-}
15−{-# LANGUAGE TypeFamilies #-}
16−{-# LANGUAGE TypeOperators #-}
17−{-# LANGUAGE UndecidableInstances #-}
18−#ifdef USE_REFLEX_OPTIMIZER
19−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20−#endif
21−module Reflex.PerformEvent.Base
22− ( PerformEventT (..)
23− , FireCommand (..)
24− , hostPerformEventT
25− ) where
26−
27−import Reflex.Class
28−import Reflex.Host.Class
29−import Reflex.PerformEvent.Class
30−import Reflex.Requester.Base
31−import Reflex.Requester.Class
32−
33−import Control.Lens
34−import Control.Monad.Exception
35−import Control.Monad.Identity
36−import Control.Monad.Primitive
37−import Control.Monad.Reader
38−import Control.Monad.Ref
39−import Data.Coerce
40−import Data.Dependent.Map (DMap)
41−import qualified Data.Dependent.Map as DMap
42−import Data.Dependent.Sum
43−
44−-- | A function that fires events for the given 'EventTrigger's and then runs
45−-- any followup actions provided via 'PerformEvent'. The given 'ReadPhase'
46−-- action will be run once for the initial trigger execution as well as once for
47−-- each followup.
48−newtype FireCommand t m = FireCommand { runFireCommand :: forall a. [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m [a] } --TODO: The handling of this ReadPhase seems wrong, or at least inelegant; how do we actually make the decision about what order frames run in?
49−
50−-- | Provides a basic implementation of 'PerformEvent'. Note that, despite the
51−-- name, 'PerformEventT' is not an instance of 'MonadTrans'.
52−newtype PerformEventT t m a = PerformEventT { unPerformEventT :: RequesterT t (HostFrame t) Identity (HostFrame t) a }
53−
54−deriving instance ReflexHost t => Functor (PerformEventT t m)
55−deriving instance ReflexHost t => Applicative (PerformEventT t m)
56−deriving instance ReflexHost t => Monad (PerformEventT t m)
57−deriving instance ReflexHost t => MonadFix (PerformEventT t m)
58−deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
59−deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
60−
61−instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m) where
62− type PrimState (PerformEventT t m) = PrimState (HostFrame t)
63− primitive = PerformEventT . lift . primitive
64−
65−instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent t (PerformEventT t m) where
66− type Performable (PerformEventT t m) = HostFrame t
67− {-# INLINABLE performEvent_ #-}
68− performEvent_ = PerformEventT . requesting_
69− {-# INLINABLE performEvent #-}
70− performEvent = PerformEventT . requestingIdentity
71−
72−instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
73− runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequesterTWith f (coerce outerA0) (coerceEvent outerA')
74− where f :: HostFrame t a -> Event t (HostFrame t b) -> RequesterT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
75− f a0 a' = do
76− result0 <- lift a0
77− result' <- requestingIdentity a'
78− return (result0, result')
79− traverseDMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ sequenceDMapWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithKey) mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
80− traverseDMapWithKeyWithAdjustWithMove f outerDm0 outerDm' = PerformEventT $ sequenceDMapWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithMoveWithKey) mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
81−
82−defaultAdjustBase :: forall t v v2 k' p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
83− => ((forall a. k' a -> v a -> HostFrame t (v2 a)) -> p k' v -> HostFrame t (p k' v2))
84− -> (forall a. k' a -> v a -> HostFrame t (v2 a))
85− -> DMap k' v
86− -> Event t (p k' v)
87− -> RequesterT t (HostFrame t) Identity (HostFrame t) (DMap k' v2, Event t (p k' v2))
88−defaultAdjustBase traversePatchWithKey f' dm0 dm' = do
89− result0 <- lift $ DMap.traverseWithKey f' dm0
90− result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
91− return (result0, result')
92−
93−instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
94− {-# INLINABLE newEventWithTrigger #-}
95− newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
96− {-# INLINABLE newFanEventWithTrigger #-}
97− newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
98−
99−-- | Run a 'PerformEventT' action, returning a 'FireCommand' that allows the
100−-- caller to trigger 'Event's while ensuring that 'performEvent' actions are run
101−-- at the appropriate time.
102−{-# INLINABLE hostPerformEventT #-}
103−hostPerformEventT :: forall t m a.
104− ( Monad m
105− , MonadSubscribeEvent t m
106− , MonadReflexHost t m
107− , MonadRef m
108− , Ref m ~ Ref IO
109− )
110− => PerformEventT t m a
111− -> m (a, FireCommand t m)
112−hostPerformEventT a = do
113− (response, responseTrigger) <- newEventWithTriggerRef
114− (result, eventToPerform) <- runHostFrame $ runRequesterT (unPerformEventT a) response
115− eventToPerformHandle <- subscribeEvent eventToPerform
116− return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
117− let go :: [DSum (EventTrigger t) Identity] -> m [a']
118− go ts = do
119− (result', mToPerform) <- fireEventsAndRead ts $ do
120− mToPerform <- sequence =<< readEvent eventToPerformHandle
121− result' <- readPhase
122− return (result', mToPerform)
123− case mToPerform of
124− Nothing -> return [result']
125− Just toPerform -> do
126− responses <- runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
127− mrt <- readRef responseTrigger
128− let followupEventTriggers = case mrt of
129− Just rt -> [rt :=> Identity responses]
130− Nothing -> []
131− (result':) <$> go followupEventTriggers
132− go triggers
133−
134−instance ReflexHost t => MonadSample t (PerformEventT t m) where
135− {-# INLINABLE sample #-}
136− sample = PerformEventT . lift . sample
137−
138−instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
139− {-# INLINABLE hold #-}
140− hold v0 v' = PerformEventT $ lift $ hold v0 v'
141− {-# INLINABLE holdDyn #-}
142− holdDyn v0 v' = PerformEventT $ lift $ holdDyn v0 v'
143− {-# INLINABLE holdIncremental #-}
144− holdIncremental v0 v' = PerformEventT $ lift $ holdIncremental v0 v'
145−
146−instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
147− type Ref (PerformEventT t m) = Ref (HostFrame t)
148− {-# INLINABLE newRef #-}
149− newRef = PerformEventT . lift . newRef
150− {-# INLINABLE readRef #-}
151− readRef = PerformEventT . lift . readRef
152− {-# INLINABLE writeRef #-}
153− writeRef r = PerformEventT . lift . writeRef r
154−
155−instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
156− {-# INLINABLE atomicModifyRef #-}
157− atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
src/Reflex/PerformEvent/Class.hsdeleted−64
@@ -1,64 +0,0 @@
1−-- | This module defines 'PerformEvent' and 'TriggerEvent', which mediate the
2−-- interaction between a "Reflex"-based program and the external side-effecting
3−-- actions such as 'IO'.
4−{-# LANGUAGE CPP #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE RankNTypes #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.PerformEvent.Class
18− ( PerformEvent (..)
19− , performEventAsync
20− ) where
21−
22−import Reflex.Class
23−import Reflex.TriggerEvent.Class
24−
25−import Control.Monad.Reader
26−
27−-- | 'PerformEvent' represents actions that can trigger other actions based on
28−-- 'Event's.
29−class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
30− -- | The type of action to be triggered; this is often not the same type as
31− -- the triggering action.
32− type Performable m :: * -> *
33− -- | Perform the action contained in the given 'Event' whenever the 'Event'
34− -- fires. Return the result in another 'Event'. Note that the output 'Event'
35− -- will generally occur later than the input 'Event', since most 'Performable'
36− -- actions cannot be performed during 'Event' propagation.
37− performEvent :: Event t (Performable m a) -> m (Event t a)
38− -- | Like 'performEvent', but do not return the result. May have slightly
39− -- better performance.
40− performEvent_ :: Event t (Performable m ()) -> m ()
41−
42−-- | Like 'performEvent', but the resulting 'Event' occurs only when the
43−-- callback (@a -> IO ()@) is called, not when the included action finishes.
44−--
45−-- NOTE: Despite the name, 'performEventAsync' does not run its action in a
46−-- separate thread - although the action is free to invoke forkIO and then call
47−-- the callback whenever it is ready. This will work properly, even in GHCJS
48−-- (which fully implements concurrency even though JavaScript does not have
49−-- built in concurrency).
50−{-# INLINABLE performEventAsync #-}
51−performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
52−performEventAsync e = do
53− (eOut, triggerEOut) <- newTriggerEvent
54− performEvent_ $ fmap ($ triggerEOut) e
55− return eOut
56−
57−instance PerformEvent t m => PerformEvent t (ReaderT r m) where
58− type Performable (ReaderT r m) = ReaderT r (Performable m)
59− performEvent_ e = do
60− r <- ask
61− lift $ performEvent_ $ flip runReaderT r <$> e
62− performEvent e = do
63− r <- ask
64− lift $ performEvent $ flip runReaderT r <$> e
src/Reflex/PostBuild/Base.hsdeleted−142
@@ -1,142 +0,0 @@
1−-- | This module defines 'PostBuildT', the standard implementation of
2−-- 'PostBuild'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DataKinds #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RankNTypes #-}
9−{-# LANGUAGE RecursiveDo #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.PostBuild.Base
18− ( PostBuildT (..)
19− , runPostBuildT
20− ) where
21−
22−import Reflex.Class
23−import Reflex.Host.Class
24−import Reflex.PerformEvent.Class
25−import Reflex.PostBuild.Class
26−import Reflex.TriggerEvent.Class
27−
28−import Control.Monad.Exception
29−import Control.Monad.Identity
30−import Control.Monad.Primitive
31−import Control.Monad.Reader
32−import Control.Monad.Ref
33−import Control.Monad.Trans.Control
34−import Data.Dependent.Map (DMap)
35−import qualified Data.Dependent.Map as DMap
36−import Data.Functor.Compose
37−
38−-- | Provides a basic implementation of 'PostBuild'.
39−newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
40−
41−-- | Run a 'PostBuildT' action. An 'Event' should be provided that fires
42−-- immediately after the action is finished running; no other 'Event's should
43−-- fire first.
44−{-# INLINABLE runPostBuildT #-}
45−runPostBuildT :: PostBuildT t m a -> Event t () -> m a
46−runPostBuildT (PostBuildT a) = runReaderT a
47−
48−instance PrimMonad m => PrimMonad (PostBuildT x m) where
49− type PrimState (PostBuildT x m) = PrimState m
50− primitive = lift . primitive
51−
52−instance MonadTransControl (PostBuildT t) where
53− type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
54− {-# INLINABLE liftWith #-}
55− liftWith = defaultLiftWith PostBuildT unPostBuildT
56− {-# INLINABLE restoreT #-}
57− restoreT = defaultRestoreT PostBuildT
58−
59−instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
60− {-# INLINABLE getPostBuild #-}
61− getPostBuild = PostBuildT ask
62−
63−instance MonadSample t m => MonadSample t (PostBuildT t m) where
64− {-# INLINABLE sample #-}
65− sample = lift . sample
66−
67−instance MonadHold t m => MonadHold t (PostBuildT t m) where
68− {-# INLINABLE hold #-}
69− hold v0 = lift . hold v0
70− {-# INLINABLE holdDyn #-}
71− holdDyn v0 = lift . holdDyn v0
72− {-# INLINABLE holdIncremental #-}
73− holdIncremental v0 = lift . holdIncremental v0
74−
75−instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
76− type Performable (PostBuildT t m) = PostBuildT t (Performable m)
77− {-# INLINABLE performEvent_ #-}
78− performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
79− {-# INLINABLE performEvent #-}
80− performEvent e = liftWith $ \run -> performEvent $ fmap run e
81−
82−instance (ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (PostBuildT t m) where
83− {-# INLINABLE newEventWithTrigger #-}
84− newEventWithTrigger = PostBuildT . lift . newEventWithTrigger
85− {-# INLINABLE newFanEventWithTrigger #-}
86− newFanEventWithTrigger f = PostBuildT $ lift $ newFanEventWithTrigger f
87−
88−instance TriggerEvent t m => TriggerEvent t (PostBuildT t m) where
89− {-# INLINABLE newTriggerEvent #-}
90− newTriggerEvent = lift newTriggerEvent
91− {-# INLINABLE newTriggerEventWithOnComplete #-}
92− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
93− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
94−
95−instance MonadRef m => MonadRef (PostBuildT t m) where
96− type Ref (PostBuildT t m) = Ref m
97− {-# INLINABLE newRef #-}
98− newRef = lift . newRef
99− {-# INLINABLE readRef #-}
100− readRef = lift . readRef
101− {-# INLINABLE writeRef #-}
102− writeRef r = lift . writeRef r
103−
104−instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
105− {-# INLINABLE atomicModifyRef #-}
106− atomicModifyRef r = lift . atomicModifyRef r
107−
108−instance (Reflex t, MonadHold t m, MonadFix m, MonadAdjust t m, PerformEvent t m) => MonadAdjust t (PostBuildT t m) where
109− runWithReplace a0 a' = do
110− postBuild <- getPostBuild
111− lift $ do
112− rec result@(_, result') <- runWithReplace (runPostBuildT a0 postBuild) $ fmap (\v -> runPostBuildT v =<< headE voidResult') a'
113− let voidResult' = fmapCheap (\_ -> ()) result'
114− return result
115− traverseDMapWithKeyWithAdjust = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap
116− traverseDMapWithKeyWithAdjustWithMove = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove
117−
118−mapDMapWithAdjustImpl :: forall t m k v v' p. (Reflex t, MonadFix m, MonadHold t m)
119− => ( (forall a. k a -> Compose ((,) (Bool, Event t ())) v a -> m (v' a))
120− -> DMap k (Compose ((,) (Bool, Event t ())) v)
121− -> Event t (p k (Compose ((,) (Bool, Event t ())) v))
122− -> m (DMap k v', Event t (p k v'))
123− )
124− -> ((forall a. v a -> Compose ((,) (Bool, Event t ())) v a) -> p k v -> p k (Compose ((,) (Bool, Event t ())) v))
125− -> (forall a. k a -> v a -> PostBuildT t m (v' a))
126− -> DMap k v
127− -> Event t (p k v)
128− -> PostBuildT t m (DMap k v', Event t (p k v'))
129−mapDMapWithAdjustImpl base mapPatch f dm0 dm' = do
130− postBuild <- getPostBuild
131− let loweredDm0 = DMap.map (Compose . (,) (False, postBuild)) dm0
132− f' :: forall a. k a -> Compose ((,) (Bool, Event t ())) v a -> m (v' a)
133− f' k (Compose ((shouldHeadE, e), v)) = do
134− eOnce <- if shouldHeadE
135− then headE e --TODO: Avoid doing this headE so many times; once per loweredDm' firing ought to be OK, but it's not totally trivial to do because result' might be firing at the same time, and we don't want *that* to be the postBuild occurrence
136− else return e
137− runPostBuildT (f k v) eOnce
138− lift $ do
139− rec (result0, result') <- base f' loweredDm0 loweredDm'
140− let voidResult' = fmapCheap (\_ -> ()) result'
141− let loweredDm' = ffor dm' $ mapPatch (Compose . (,) (True, voidResult'))
142− return (result0, result')
src/Reflex/PostBuild/Class.hsdeleted−34
@@ -1,34 +0,0 @@
1−-- | This module defines 'PostBuild', which indicates that an action will be
2−-- notified when it, and any action it's a part of, has finished executing.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DataKinds #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−#ifdef USE_REFLEX_OPTIMIZER
13−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
14−#endif
15−module Reflex.PostBuild.Class
16− ( PostBuild (..)
17− ) where
18−
19−import Reflex.Class
20−
21−import Control.Monad.Reader
22−
23−-- | 'PostBuild' represents an action that is notified via an 'Event' when it
24−-- has finished executing. Note that the specific definition of "finished" is
25−-- determined by the instance of 'PostBuild', but the intent is to allow
26−-- 'Behavior's and 'Dynamic's to be safely sampled, regardless of where they
27−-- were created, when the post-build 'Event' fires. The post-build 'Event' will
28−-- fire exactly once for an given action.
29−class (Reflex t, Monad m) => PostBuild t m | m -> t where
30− -- | Retrieve the post-build 'Event' for this action.
31− getPostBuild :: m (Event t ())
32−
33−instance PostBuild t m => PostBuild t (ReaderT r m) where
34− getPostBuild = lift getPostBuild
src/Reflex/Pure.hsdeleted−189
@@ -1,189 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE EmptyDataDecls #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE InstanceSigs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−#ifdef USE_REFLEX_OPTIMIZER
9−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10−#endif
11−
12−-- There are two expected orphan instances in this module:
13−-- * MonadSample (Pure t) ((->) t)
14−-- * MonadHold (Pure t) ((->) t)
15−{-# OPTIONS_GHC -fno-warn-orphans #-}
16−
17−-- | This module provides a pure implementation of Reflex, which is intended to
18−-- serve as a reference for the semantics of the Reflex class. All
19−-- implementations of Reflex should produce the same results as this
20−-- implementation, although performance and laziness/strictness may differ.
21−module Reflex.Pure
22− ( Pure
23− , Behavior (..)
24− , Event (..)
25− , Dynamic (..)
26− , Incremental (..)
27− ) where
28−
29−#if !MIN_VERSION_base(4,8,0)
30−import Control.Applicative
31−#endif
32−
33−import Control.Monad
34−import Data.Dependent.Map (DMap, GCompare)
35−import qualified Data.Dependent.Map as DMap
36−import Data.Functor.Identity
37−import Data.Maybe
38−import Data.MemoTrie
39−import Data.Monoid
40−import Data.Type.Coercion
41−import Reflex.Class
42−
43−-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
44−-- with the type @t@.
45−data Pure t
46−
47−-- | The Enum instance of t must be dense: for all x :: t, there must not exist
48−-- any y :: t such that pred x < y < x. The HasTrie instance will be used
49−-- exclusively to memoize functions of t, not for any of its other capabilities.
50−instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
51−
52− newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
53− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
54− newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
55− newtype Incremental (Pure t) p = Incremental { unIncremental :: t -> (PatchTarget p, Maybe p) }
56−
57− type PushM (Pure t) = (->) t
58− type PullM (Pure t) = (->) t
59−
60− never :: Event (Pure t) a
61− never = Event $ \_ -> Nothing
62−
63− constant :: a -> Behavior (Pure t) a
64− constant x = Behavior $ \_ -> x
65−
66− push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
67− push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
68−
69− pushCheap :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
70− pushCheap = push
71−
72− pull :: PullM (Pure t) a -> Behavior (Pure t) a
73− pull = Behavior . memo
74−
75− -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
76− -- warning because the GCompare instance is not used; however, removing the
77− -- GCompare instance produces a different warning, due to that constraint
78− -- being present in the original class definition
79−
80− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
81− merge events = Event $ memo $ \t ->
82− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
83− in if DMap.null currentOccurrences
84− then Nothing
85− else Just currentOccurrences
86−
87− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
88− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
89−
90− switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
91− switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
92−
93− coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
94− coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
95−
96− current :: Dynamic (Pure t) a -> Behavior (Pure t) a
97− current d = Behavior $ \t -> fst $ unDynamic d t
98−
99− updated :: Dynamic (Pure t) a -> Event (Pure t) a
100− updated d = Event $ \t -> snd $ unDynamic d t
101−
102− unsafeBuildDynamic :: PullM (Pure t) a -> Event (Pure t) a -> Dynamic (Pure t) a
103− unsafeBuildDynamic readV0 v' = Dynamic $ \t -> (readV0 t, unEvent v' t)
104−
105− -- See UNUSED_CONSTRAINT, above.
106−
107− --unsafeBuildIncremental :: Patch p => PullM (Pure t) a -> Event (Pure t) (p
108− --a) -> Incremental (Pure t) p a
109− unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
110−
111− mergeIncremental = mergeIncrementalImpl
112− mergeIncrementalWithMove = mergeIncrementalImpl
113−
114− currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
115−
116− updatedIncremental i = Event $ \t -> snd $ unIncremental i t
117−
118− incrementalToDynamic i = Dynamic $ \t ->
119− let (old, mPatch) = unIncremental i t
120− e = case mPatch of
121− Nothing -> Nothing
122− Just patch -> apply patch old
123− in (old, e)
124− behaviorCoercion Coercion = Coercion
125− eventCoercion Coercion = Coercion
126− dynamicCoercion Coercion = Coercion
127−
128−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
129−mergeIncrementalImpl i = Event $ \t ->
130− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
131− in if DMap.null results
132− then Nothing
133− else Just results
134−
135−instance Functor (Dynamic (Pure t)) where
136− fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
137− in (f cur, fmap f upd)
138−
139−instance Applicative (Dynamic (Pure t)) where
140− pure a = Dynamic $ \_ -> (a, Nothing)
141− (<*>) = ap
142−
143−instance Monad (Dynamic (Pure t)) where
144− return = pure
145− (x :: Dynamic (Pure t) a) >>= (f :: a -> Dynamic (Pure t) b) = Dynamic $ \t ->
146− let (curX :: a, updX :: Maybe a) = unDynamic x t
147− (cur :: b, updOuter :: Maybe b) = unDynamic (f curX) t
148− (updInner :: Maybe b, updBoth :: Maybe b) = case updX of
149− Nothing -> (Nothing, Nothing)
150− Just nextX -> let (c, u) = unDynamic (f nextX) t
151− in (Just c, u)
152− in (cur, getFirst $ mconcat $ map First [updBoth, updOuter, updInner])
153−
154−instance MonadSample (Pure t) ((->) t) where
155−
156− sample :: Behavior (Pure t) a -> (t -> a)
157− sample = unBehavior
158−
159−instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
160−
161− hold :: a -> Event (Pure t) a -> t -> Behavior (Pure t) a
162− hold initialValue e initialTime = Behavior f
163− where f = memo $ \sampleTime ->
164− -- Really, the sampleTime should never be prior to the initialTime,
165− -- because that would mean the Behavior is being sampled before
166− -- being created.
167− if sampleTime <= initialTime
168− then initialValue
169− else let lastTime = pred sampleTime
170− in fromMaybe (f lastTime) $ unEvent e lastTime
171−
172− holdDyn :: a -> Event (Pure t) a -> t -> Dynamic (Pure t) a
173− holdDyn initialValue e initialTime =
174− let Behavior f = hold initialValue e initialTime
175− in Dynamic $ \t -> (f t, unEvent e t)
176−
177− holdIncremental :: Patch p => PatchTarget p -> Event (Pure t) p -> t -> Incremental (Pure t) p
178− holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
179− where f = memo $ \sampleTime ->
180− -- Really, the sampleTime should never be prior to the initialTime,
181− -- because that would mean the Behavior is being sampled before
182− -- being created.
183− if sampleTime <= initialTime
184− then initialValue
185− else let lastTime = pred sampleTime
186− lastValue = f lastTime
187− in case unEvent e lastTime of
188− Nothing -> lastValue
189− Just x -> fromMaybe lastValue $ apply x lastValue
src/Reflex/Requester/Base.hsdeleted−151
@@ -1,151 +0,0 @@
1−-- | This module provides 'RequesterT', the standard implementation of
2−-- 'Requester'.
3−{-# LANGUAGE BangPatterns #-}
4−{-# LANGUAGE CPP #-}
5−{-# LANGUAGE ExistentialQuantification #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−{-# LANGUAGE StandaloneDeriving #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.Requester.Base
18− ( RequesterT (..)
19− , runRequesterT
20− , runWithReplaceRequesterTWith
21− , sequenceDMapWithAdjustRequesterTWith
22− ) where
23−
24−import Reflex.Class
25−import Reflex.EventWriter
26−import Reflex.Host.Class
27−import Reflex.PerformEvent.Class
28−import Reflex.PostBuild.Class
29−import Reflex.Requester.Class
30−import Reflex.TriggerEvent.Class
31−
32−import Control.Monad.Exception
33−import Control.Monad.Identity
34−import Control.Monad.Primitive
35−import Control.Monad.Reader
36−import Control.Monad.Ref
37−import Control.Monad.State.Strict
38−import Data.Coerce
39−import Data.Dependent.Map (DMap, DSum (..))
40−import qualified Data.Dependent.Map as DMap
41−import Data.Functor.Misc
42−import Data.Map (Map)
43−import Data.Unique.Tag
44−import Data.Some (Some)
45−
46−-- | A basic implementation of 'Requester'.
47−newtype RequesterT t request response m a = RequesterT { unRequesterT :: EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) a }
48− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException)
49−
50−deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
51−deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
52−deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
53−deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
54−
55−-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
56−-- requests are made, and responses should be provided in the input 'Event'.
57−-- The 'Tag' keys will be used to return the responses to the same place the
58−-- requests were issued.
59−
60−runRequesterT :: (Reflex t, Monad m)
61− => RequesterT t request response m a
62− -> Event t (DMap (Tag (PrimState m)) response)
63− -> m (a, Event t (DMap (Tag (PrimState m)) request))
64−runRequesterT (RequesterT a) responses = do
65− (result, requests) <- runReaderT (runEventWriterT a) $ fan $
66− mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e) <$> responses
67− return (result, requests)
68−
69−instance (Reflex t, PrimMonad m) => Requester t (RequesterT t request response m) where
70− type Request (RequesterT t request response m) = request
71− type Response (RequesterT t request response m) = response
72− withRequesting a = do
73− !t <- lift newTag --TODO: Fix this upstream
74− s <- RequesterT ask
75− (req, result) <- a $ select s $ WrapArg t
76− RequesterT $ tellEvent $ fmapCheap (DMap.singleton t) req
77− return result
78−
79−instance MonadTrans (RequesterT t request response) where
80− lift = RequesterT . lift . lift
81−
82−instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
83− type Performable (RequesterT t request response m) = Performable m
84− performEvent_ = lift . performEvent_
85− performEvent = lift . performEvent
86−
87−instance MonadRef m => MonadRef (RequesterT t request response m) where
88− type Ref (RequesterT t request response m) = Ref m
89− newRef = lift . newRef
90− readRef = lift . readRef
91− writeRef r = lift . writeRef r
92−
93−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
94− newEventWithTrigger = lift . newEventWithTrigger
95− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
96−
97−instance MonadReader r m => MonadReader r (RequesterT t request response m) where
98− ask = lift ask
99− local f (RequesterT (EventWriterT a)) = RequesterT $ EventWriterT $ mapStateT (mapReaderT $ local f) a
100− reader = lift . reader
101−
102−instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (RequesterT t request response m) where
103− runWithReplace (RequesterT a) em = RequesterT $ runWithReplace a (coerceEvent em)
104− traverseDMapWithKeyWithAdjust f dm edm = RequesterT $ traverseDMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) (coerce dm) (coerceEvent edm)
105− traverseDMapWithKeyWithAdjustWithMove f dm edm = RequesterT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRequesterT $ f k v) (coerce dm) (coerceEvent edm)
106−
107−runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m)
108− => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
109− -> RequesterT t request response m a
110− -> Event t (RequesterT t request response m b)
111− -> RequesterT t request response m (a, Event t b)
112−runWithReplaceRequesterTWith f a0 a' =
113− let f' :: forall a' b'. ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m a'
114− -> Event t (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m b')
115− -> EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) (a', Event t b')
116− f' x y = do
117− r <- EventWriterT $ ask
118− unRequesterT (f (runReaderT x r) (fmapCheap (`runReaderT` r) y))
119− in RequesterT $ runWithReplaceEventWriterTWith f' (coerce a0) (coerceEvent a')
120−
121−sequenceDMapWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
122− ( GCompare k
123− , Reflex t
124− , MonadHold t m
125− , PatchTarget (p' (Some k) (Event t (DMap (Tag (PrimState m)) request))) ~ Map (Some k) (Event t (DMap (Tag (PrimState m)) request))
126− , Patch (p' (Some k) (Event t (DMap (Tag (PrimState m)) request)))
127− )
128− => (forall k' v1 v2. GCompare k'
129− => (forall a. k' a -> v1 a -> m (v2 a))
130− -> DMap k' v1
131− -> Event t (p k' v1)
132− -> RequesterT t request response m (DMap k' v2, Event t (p k' v2))
133− )
134− -> (forall v1. (forall a. v1 a -> v' a) -> p k v1 -> p k v')
135− -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
136− -> (forall v2. p' (Some k) v2 -> [v2])
137− -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
138− -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
139− -> DMap k v
140− -> Event t (p k v)
141− -> RequesterT t request response m (DMap k v', Event t (p k v'))
142−sequenceDMapWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' =
143− let base' :: forall v1 v2.
144− (forall a. k a -> v1 a -> ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m (v2 a))
145− -> DMap k v1
146− -> Event t (p k v1)
147− -> EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) (DMap k v2, Event t (p k v2))
148− base' f' x y = do
149− r <- EventWriterT ask
150− unRequesterT $ base (\k v -> runReaderT (f' k v) r) x y
151− in RequesterT $ sequenceDMapWithAdjustEventWriterTWith base' mapPatch weakenPatchWith patchNewElements mergePatchIncremental (\k v -> unRequesterT $ f k v) (coerce dm0) (coerceEvent dm')
src/Reflex/Requester/Class.hsdeleted−87
@@ -1,87 +0,0 @@
1−-- | This module defines 'Requester', which indicates that an action can make
2−-- requests and receive responses to them. Typically, this is used for things
3−-- like a WebSocket, where it's desirable to collect many potential sources of
4−-- events and send them over a single channel, then distribute the results back
5−-- out efficiently to their original request sites.
6−{-# LANGUAGE CPP #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−#ifdef USE_REFLEX_OPTIMIZER
13−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
14−#endif
15−module Reflex.Requester.Class
16− ( Requester (..)
17− , requesting
18− , requesting_
19− , requestingIdentity
20− ) where
21−
22−import Control.Monad.Identity
23−import Control.Monad.Reader
24−import qualified Control.Monad.State.Lazy as Lazy
25−import Control.Monad.State.Strict
26−import Reflex.Class
27−
28−-- | A 'Requester' action can trigger requests of type @Request m a@ based on
29−-- 'Event's, and receive responses of type @Response m a@ in return. Note that
30−-- the @a@ type can vary within the 'Requester' action, but will be linked for a
31−-- given request. For example, if @Request m@ is 'IO' and @Response m@ is
32−-- 'Identity', then 'requestingIdentity' has the same type as
33−-- 'Reflex.PerformEvent.Class.performEvent'.
34−class (Reflex t, Monad m) => Requester t m | m -> t where
35− -- | The type of requests that this 'Requester' can emit
36− type Request m :: * -> *
37− -- | The type of responses that this 'Requester' can receive
38− type Response m :: * -> *
39− -- | Usually, end-user code should use 'requesting', 'requesting_', or
40− -- 'requestingIdentity'. For implementers of 'Requester', this function
41− -- provides a convenient way to define instances of the typeclass that
42− -- efficiently support the underlying operations without needing to use
43− -- 'Control.Monad.Fix.MonadFix'.
44− withRequesting :: (Event t (Response m a) -> m (Event t (Request m a), r)) -> m r
45−
46−instance Requester t m => Requester t (ReaderT r m) where
47− type Request (ReaderT r m) = Request m
48− type Response (ReaderT r m) = Response m
49− withRequesting f = do
50− r <- ask
51− lift $ withRequesting $ (`runReaderT` r) . f
52−
53−instance Requester t m => Requester t (StateT s m) where
54− type Request (StateT s m) = Request m
55− type Response (StateT s m) = Response m
56− withRequesting f = do
57− old <- get
58− (result, new) <- lift $ withRequesting $ \x -> do
59− ((request, result), new) <- runStateT (f x) old
60− return (request, (result, new))
61− put new
62− return result
63−
64−instance Requester t m => Requester t (Lazy.StateT s m) where
65− type Request (Lazy.StateT s m) = Request m
66− type Response (Lazy.StateT s m) = Response m
67− withRequesting f = do
68− old <- Lazy.get
69− (result, new) <- lift $ withRequesting $ \x -> do
70− ((request, result), new) <- Lazy.runStateT (f x) old
71− return (request, (result, new))
72− Lazy.put new
73− return result
74−
75−-- | Emit a request whenever the given 'Event' fires, and return responses in
76−-- the resulting 'Event'.
77−requesting :: Requester t m => Event t (Request m a) -> m (Event t (Response m a))
78−requesting req = withRequesting $ return . (,) req
79−
80−-- | Emit a request whenever the given 'Event' fires, and ignore all responses.
81−requesting_ :: Requester t m => Event t (Request m a) -> m ()
82−requesting_ req = withRequesting $ \_ -> return (req, ())
83−
84−-- | Emit a request whenever the given 'Event' fires, and unwrap the responses
85−-- before returning them. @Response m@ must be 'Identity'.
86−requestingIdentity :: (Requester t m, Response m ~ Identity) => Event t (Request m a) -> m (Event t a)
87−requestingIdentity = fmap coerceEvent . requesting
src/Reflex/Spider.hs+3−20
@@ -1,22 +1,5 @@
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
1+module Reflex.Spider ( Spider
2+ , SpiderHost (..) --Temporary, until Widget no longer uses unsafePerformIO
3+ ) where
184
19−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
20−import Reflex.Class
21−#endif
225import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hs+1095−1838
@@ -1,191 +1,83 @@
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 (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
1+{-# LANGUAGE CPP, ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, LambdaCase, TypeOperators #-}
2+module Reflex.Spider.Internal where
3+
4+import qualified Reflex.Class as R
5+import qualified Reflex.Host.Class as R
6+
7+import Data.IORef
8+import System.Mem.Weak
9+import Data.Foldable
10+import Data.Traversable
11+import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
12+import Control.Monad.Identity hiding (mapM, mapM_, forM_, forM, sequence)
13+import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
14+import GHC.Exts
15+import Control.Applicative -- Unconditionally import, because otherwise it breaks on GHC 7.10.1RC2
4216import Data.Dependent.Map (DMap, DSum (..))
4317import 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
4918import 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')
5419import Data.Maybe
20+import Data.IntMap (IntMap)
21+import qualified Data.IntMap as IntMap
22+import Control.Monad.Ref
23+import Control.Monad.Exception
5524import 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
25+import Data.Coerce
26+
6427import System.IO.Unsafe
65−import System.Mem.Weak
6628import 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, _weakBag_children)
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
29+import Control.Monad.Primitive
8830
8931-- 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−#ifdef DEBUG_TRACE_EVENTS
93−import System.IO (stderr)
94−import qualified Data.ByteString.Char8 as BS8
95−#endif
96−
97−#ifdef DEBUG_TRACE_EVENTS
98−
99−withStackOneLine :: (BS8.ByteString -> a) -> a
100−withStackOneLine expr = unsafePerformIO $ do
101− stack <- currentCallStack
102− return (expr (BS8.pack (unwords (reverse stack))))
103−
104−#endif
32+import Prelude hiding (mapM, mapM_, any, sequence, concat)
10533
10634debugPropagate :: Bool
10735
10836debugInvalidateHeight :: Bool
10937
110−debugInvalidate :: Bool
111−
11238#ifdef DEBUG
11339
11440#define DEBUG_NODEIDS
11541
11642debugPropagate = True
11743
118−debugInvalidateHeight = False
119−
120−debugInvalidate = True
44+debugInvalidateHeight = True
12145
12246class HasNodeId a where
12347 getNodeId :: a -> Int
12448
125−instance HasNodeId (Hold x p a) where
49+instance HasNodeId (Hold a) where
12650 getNodeId = holdNodeId
12751
128−instance HasNodeId (PushSubscribed x a b) where
52+instance HasNodeId (PushSubscribed a b) where
12953 getNodeId = pushSubscribedNodeId
13054
131−instance HasNodeId (SwitchSubscribed x a) where
55+instance HasNodeId (SwitchSubscribed a) where
13256 getNodeId = switchSubscribedNodeId
13357
134−instance HasNodeId (FanSubscribed x a) where
58+instance HasNodeId (MergeSubscribed a) where
59+ getNodeId = mergeSubscribedNodeId
60+
61+instance HasNodeId (FanSubscribed a) where
13562 getNodeId = fanSubscribedNodeId
13663
137−instance HasNodeId (CoincidenceSubscribed x a) where
64+instance HasNodeId (CoincidenceSubscribed a) where
13865 getNodeId = coincidenceSubscribedNodeId
13966
140−instance HasNodeId (RootSubscribed x a) where
67+instance HasNodeId (RootSubscribed a) where
14168 getNodeId = rootSubscribedNodeId
14269
143−instance HasNodeId (Pull x a) where
144− getNodeId = pullNodeId
145−
146−{-# INLINE showNodeId #-}
14770showNodeId :: HasNodeId a => a -> String
14871showNodeId = ("#"<>) . show . getNodeId
14972
150−showSubscriberType :: Subscriber x a -> String
151−showSubscriberType = \case
152− SubscriberPush _ _ -> "SubscriberPush"
153− SubscriberMerge _ _ -> "SubscriberMerge"
154− SubscriberFan _ -> "SubscriberFan"
155− SubscriberMergeChange _ -> "SubscriberMergeChange"
156− SubscriberHold _ -> "SubscriberHold"
157− SubscriberHoldIdentity _ -> "SubscriberHoldIdentity"
158− SubscriberSwitch _ -> "SubscriberSwitch"
159− SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
160− SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
161− SubscriberHandle -> "SubscriberHandle"
162−
163−showEventType :: Event x a -> String
164−showEventType = \case
165− EventRoot _ _ -> "EventRoot"
166− EventNever -> "EventNever"
167− EventPush _ -> "EventPush"
168− EventMerge _ -> "EventMerge"
169− EventFan _ _ -> "EventFan"
170− EventSwitch _ -> "EventSwitch"
171− EventCoincidence _ -> "EventCoincidence"
172− EventHold _ -> "EventHold"
173− EventDyn _ -> "EventDyn"
174− EventHoldIdentity _ -> "EventHoldIdentity"
175− EventDynIdentity _ -> "EventDynIdentity"
176−
17773#else
17874
17975debugPropagate = False
18076
18177debugInvalidateHeight = False
18278
183−debugInvalidate = False
184−
185−-- This must be inline, or error messages will cause memory leaks due to retaining the node in question
186−{-# INLINE showNodeId #-}
18779showNodeId :: a -> String
188−showNodeId _ = ""
80+showNodeId = const ""
18981
19082#endif
19183
@@ -194,922 +86,377 @@ showNodeId _ = ""
19486nextNodeIdRef :: IORef Int
19587nextNodeIdRef = unsafePerformIO $ newIORef 1
19688
197−newNodeId :: IO Int
198−newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
199−
20089{-# NOINLINE unsafeNodeId #-}
20190unsafeNodeId :: a -> Int
20291unsafeNodeId a = unsafePerformIO $ do
20392 touch a
204− newNodeId
205−#endif
206−
207−--------------------------------------------------------------------------------
208−-- EventSubscription
209−--------------------------------------------------------------------------------
210−
211−--NB: Once you subscribe to an Event, you must always hold on the the WHOLE EventSubscription you get back
212−-- If you do not retain the subscription, you may be prematurely unsubscribed from the parent event.
213−data EventSubscription x = EventSubscription
214− { _eventSubscription_unsubscribe :: !(IO ())
215− , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
216− }
217−
218−unsubscribe :: EventSubscription x -> IO ()
219−unsubscribe (EventSubscription u _) = u
220−
221−--------------------------------------------------------------------------------
222−-- Event
223−--------------------------------------------------------------------------------
224−
225−newtype Event x a = Event { unEvent :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
226−
227−{-# INLINE subscribeAndRead #-}
228−subscribeAndRead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
229−subscribeAndRead = unEvent
230−
231−{-# RULES
232−"cacheEvent/cacheEvent" forall e. cacheEvent (cacheEvent e) = cacheEvent e
233−"cacheEvent/pushCheap" forall f e. pushCheap f (cacheEvent e) = cacheEvent (pushCheap f e)
234−"hold/cacheEvent" forall f e. hold f (cacheEvent e) = hold f e
235− #-}
236−
237−-- | Construct an 'Event' equivalent to that constructed by 'push', but with no
238−-- caching; if the computation function is very cheap, this is (much) more
239−-- efficient than 'push'
240−{-# NOINLINE pushCheap #-}
241−pushCheap :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
242−pushCheap f e = Event $ \sub -> do
243− (subscription, occ) <- subscribeAndRead e $ sub
244− { subscriberPropagate = \a -> do
245− mb <- runComputeM $ f a
246− mapM_ (subscriberPropagate sub) mb
247− }
248− occ' <- join <$> mapM (runComputeM . f) occ
249− return (subscription, occ')
250−
251−data CacheSubscribed x a
252− = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(WeakBag (Subscriber x a))
253− , _cacheSubscribed_weakSelf :: {-# UNPACK #-} !(IORef (Weak (CacheSubscribed x a)))
254− , _cacheSubscribed_parent :: {-# UNPACK #-} !(EventSubscription x)
255− , _cacheSubscribed_cached :: {-# UNPACK #-} !(IORef (Maybe (CacheSubscribed x a)))
256− , _cacheSubscribed_occurrence :: {-# UNPACK #-} !(IORef (Maybe a))
257− }
258−
259−-- | Construct an 'Event' whose value is guaranteed not to be recomputed
260−-- repeatedly
261−--
262−--TODO: Try a caching strategy where we subscribe directly to the parent when
263−--there's only one subscriber, and then build our own WeakBag only when a second
264−--subscriber joins
265−{-# NOINLINE cacheEvent #-}
266−cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
267−cacheEvent e =
268−#ifdef DEBUG_TRACE_EVENTS
269− withStackOneLine $ \callSite -> Event $
270−#else
271− Event $
93+ atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
27294#endif
273− let !mSubscribedRef = unsafeNewIORef e Nothing
274− in \sub -> {-# SCC "cacheEvent" #-} do
275−#ifdef DEBUG_TRACE_EVENTS
276− unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
277−#endif
278− mSubscribed <- liftIO $ readIORef mSubscribedRef
279− case mSubscribed of
280− Just subscribed -> liftIO $ do
281− ticket <- WeakBag.insert sub (_cacheSubscribed_subscribers subscribed) (_cacheSubscribed_weakSelf subscribed) cleanupCacheSubscribed
282− occ <- readIORef $ _cacheSubscribed_occurrence subscribed
283− let es = EventSubscribed
284− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
285− , eventSubscribedRetained = toAny subscribed
286− }
287− return (EventSubscription (WeakBag.remove ticket >> touch ticket) es, occ)
288− Nothing -> do
289− weakSelf <- liftIO $ newIORef $ error "cacheEvent: weakSelf not yet intialized"
290− (subscribers, ticket) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCacheSubscribed
291− occRef <- liftIO $ newIORef undefined
292− (parentSub, occ) <- subscribeAndRead e $ Subscriber
293− { subscriberPropagate = \a -> do
294− liftIO $ writeIORef occRef $ Just a
295− scheduleClear occRef
296− propagate a subscribers
297− , subscriberInvalidateHeight = \old -> do
298− WeakBag.traverse subscribers $ invalidateSubscriberHeight old
299− , subscriberRecalculateHeight = \new -> do
300− WeakBag.traverse subscribers $ recalculateSubscriberHeight new
301− }
302− liftIO $ writeIORef occRef occ
303− when (isJust occ) $ scheduleClear occRef
304− let !subscribed = CacheSubscribed
305− { _cacheSubscribed_subscribers = subscribers
306− , _cacheSubscribed_weakSelf = weakSelf
307− , _cacheSubscribed_parent = parentSub
308− , _cacheSubscribed_cached = mSubscribedRef
309− , _cacheSubscribed_occurrence = occRef
310− }
311− liftIO $ writeIORef mSubscribedRef $ Just subscribed
312− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
313− return (EventSubscription (WeakBag.remove ticket >> touch ticket) (EventSubscribed (eventSubscribedHeightRef $ _eventSubscription_subscribed parentSub) (toAny subscribed)), occ)
314−
315−cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
316−cleanupCacheSubscribed self = do
317− unsubscribe $ _cacheSubscribed_parent self
318− writeIORef (_cacheSubscribed_cached self) Nothing
319−
320−subscribe :: Event x a -> Subscriber x a -> EventM x (EventSubscription x)
321−subscribe e s = fst <$> subscribeAndRead e s
322−
323−{-# INLINE wrap #-}
324−wrap :: MonadIO m => (t -> EventSubscribed x) -> (Subscriber x a -> m (WeakBagTicket, t, Maybe a)) -> Subscriber x a -> m (EventSubscription x, Maybe a)
325−wrap tag getSpecificSubscribed sub = do
326− (sln, subd, occ) <- getSpecificSubscribed sub
327− let es = tag subd
328− return (EventSubscription (WeakBag.remove sln >> touch sln) es, occ)
329−
330−eventRoot :: GCompare k => k a -> Root x k -> Event x a
331−eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed k r
332−
333−eventNever :: Event x a
334−eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
335−
336−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
337−eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
338−
339−eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
340−eventSwitch !s = Event $ wrap eventSubscribedSwitch $ getSwitchSubscribed s
341−
342−eventCoincidence :: HasSpiderTimeline x => Coincidence x a -> Event x a
343−eventCoincidence !c = Event $ wrap eventSubscribedCoincidence $ getCoincidenceSubscribed c
344−
345−eventHold :: HasSpiderTimeline x => Hold x p -> Event x p
346−eventHold !h = Event $ subscribeHoldEvent h
347−
348−eventDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Event x p
349−eventDyn !j = Event $ \sub -> getDynHold j >>= \h -> subscribeHoldEvent h sub
350−
351−{-# INLINE subscribeCoincidenceInner #-}
352−subscribeCoincidenceInner :: CanSubscribe x m => Event x a -> Height -> CoincidenceSubscribed x a -> m (Maybe a, Height, EventSubscribed x)
353−subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
354− subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
355− (subscription@(EventSubscription _ innerSubd), innerOcc) <- subscribeAndRead inner subInner
356− innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
357− let height = max innerHeight outerHeight
358− defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
359− return (innerOcc, height, innerSubd)
360−
361−--------------------------------------------------------------------------------
362−-- Subscriber
363−--------------------------------------------------------------------------------
364−
365−data Subscriber x a = Subscriber
366− { subscriberPropagate :: !(a -> EventM x ())
367− , subscriberInvalidateHeight :: !(Height -> IO ())
368− , subscriberRecalculateHeight :: !(Height -> IO ())
369− }
370−
371−--TODO: Move this comment to WeakBag
372−-- These function are constructor functions that are marked NOINLINE so they are
373−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
374−-- so any weak references that are attached to the constructors will have their
375−-- finalizer run. Using the opaque constructor, does not see the
376−-- constructor application, so it behaves like an IORef and cannot be fused away.
377−--
378−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
379−-- the weak pointer to it in some cases.
380−
381−newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
382−newSubscriberHold h = return $ Subscriber
383− { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
384− , subscriberInvalidateHeight = \_ -> return ()
385− , subscriberRecalculateHeight = \_ -> return ()
386− }
387−
388−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
389−newSubscriberFan subscribed = return $ Subscriber
390− { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
391− subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
392− tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
393− liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
394− scheduleClear $ fanSubscribedOccurrence subscribed
395− let f _ (Pair (Identity v) subsubs) = do
396− propagate v $ _fanSubscribedChildren_list subsubs
397− return $ Constant ()
398− _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
399− return ()
400− , subscriberInvalidateHeight = \old -> do
401− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
402− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
403− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
404− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
405− , subscriberRecalculateHeight = \new -> do
406− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
407− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
408− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
409− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
410− }
411−
412−newSubscriberSwitch :: forall x a. HasSpiderTimeline x => SwitchSubscribed x a -> IO (Subscriber x a)
413−newSubscriberSwitch subscribed = return $ Subscriber
414− { subscriberPropagate = \a -> {-# SCC "traverseSwitch" #-} do
415− tracePropagate (Proxy :: Proxy x) $ "SubscriberSwitch" <> showNodeId subscribed
416− liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
417− scheduleClear $ switchSubscribedOccurrence subscribed
418− propagate a $ switchSubscribedSubscribers subscribed
419− , subscriberInvalidateHeight = \_ -> do
420− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
421− oldHeight <- readIORef $ switchSubscribedHeight subscribed
422− when (oldHeight /= invalidHeight) $ do
423− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
424− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
425− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
426− , subscriberRecalculateHeight = \new -> do
427− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
428− updateSwitchHeight new subscribed
429− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
430− }
431−
432−newSubscriberCoincidenceOuter :: forall x b. HasSpiderTimeline x => CoincidenceSubscribed x b -> IO (Subscriber x (Event x b))
433−newSubscriberCoincidenceOuter subscribed = return $ Subscriber
434− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceOuter" #-} do
435− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
436− outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
437− tracePropagate (Proxy :: Proxy x) $ " outerHeight = " <> show outerHeight
438− (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
439− tracePropagate (Proxy :: Proxy x) $ " isJust occ = " <> show (isJust occ)
440− tracePropagate (Proxy :: Proxy x) $ " innerHeight = " <> show innerHeight
441− liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
442− scheduleClear $ coincidenceSubscribedInnerParent subscribed
443− case occ of
444− Nothing -> do
445− when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
446− writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
447− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
448− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
449− Just o -> do -- Since it's already firing, no need to adjust height
450− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
451− scheduleClear $ coincidenceSubscribedOccurrence subscribed
452− propagate o $ coincidenceSubscribedSubscribers subscribed
453− , subscriberInvalidateHeight = \_ -> do
454− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
455− invalidateCoincidenceHeight subscribed
456− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
457− , subscriberRecalculateHeight = \_ -> do
458− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
459− recalculateCoincidenceHeight subscribed
460− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
461− }
462−
463−newSubscriberCoincidenceInner :: forall x a. HasSpiderTimeline x => CoincidenceSubscribed x a -> IO (Subscriber x a)
464−newSubscriberCoincidenceInner subscribed = return $ Subscriber
465− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceInner" #-} do
466− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceInner" <> showNodeId subscribed
467− occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
468− case occ of
469− Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
470− Nothing -> do
471− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
472− scheduleClear $ coincidenceSubscribedOccurrence subscribed
473− propagate a $ coincidenceSubscribedSubscribers subscribed
474− , subscriberInvalidateHeight = \_ -> do
475− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
476− invalidateCoincidenceHeight subscribed
477− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
478− , subscriberRecalculateHeight = \_ -> do
479− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
480− recalculateCoincidenceHeight subscribed
481− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
482− }
483−
484−invalidateSubscriberHeight :: Height -> Subscriber x a -> IO ()
485−invalidateSubscriberHeight = flip subscriberInvalidateHeight
486−
487−recalculateSubscriberHeight :: Height -> Subscriber x a -> IO ()
488−recalculateSubscriberHeight = flip subscriberRecalculateHeight
489−
490−-- | Propagate everything at the current height
491−propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
492−propagate a subscribers = withIncreasedDepth $ do
493− -- 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
494− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
495− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
496−
497−--------------------------------------------------------------------------------
498−-- EventSubscribed
499−--------------------------------------------------------------------------------
500−
501−toAny :: a -> Any
502−toAny = unsafeCoerce
503−
504−data EventSubscribed x = EventSubscribed
505− { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
506− , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
507−#ifdef DEBUG_CYCLES
508− , eventSubscribedGetParents :: !(IO [Some (EventSubscribed x)]) -- For debugging loops
509− , eventSubscribedHasOwnHeightRef :: !Bool
510− , eventSubscribedWhoCreated :: !(IO [String])
511−#endif
512− }
513−
514−eventSubscribedRoot :: RootSubscribed x a -> EventSubscribed x
515−eventSubscribedRoot !r = EventSubscribed
516− { eventSubscribedHeightRef = zeroRef
517− , eventSubscribedRetained = toAny r
518−#ifdef DEBUG_CYCLES
519− , eventSubscribedGetParents = return []
520− , eventSubscribedHasOwnHeightRef = False
521− , eventSubscribedWhoCreated = return ["root"]
522−#endif
523− }
524−
525−eventSubscribedNever :: EventSubscribed x
526−eventSubscribedNever = EventSubscribed
527− { eventSubscribedHeightRef = zeroRef
528− , eventSubscribedRetained = toAny ()
529−#ifdef DEBUG_CYCLES
530− , eventSubscribedGetParents = return []
531− , eventSubscribedHasOwnHeightRef = False
532− , eventSubscribedWhoCreated = return ["never"]
533−#endif
534− }
535−
536−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
537−eventSubscribedFan !subscribed = EventSubscribed
538− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
539− , eventSubscribedRetained = toAny subscribed
540−#ifdef DEBUG_CYCLES
541− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
542− , eventSubscribedHasOwnHeightRef = False
543− , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
544−#endif
545− }
546−
547−eventSubscribedSwitch :: SwitchSubscribed x a -> EventSubscribed x
548−eventSubscribedSwitch !subscribed = EventSubscribed
549− { eventSubscribedHeightRef = switchSubscribedHeight subscribed
550− , eventSubscribedRetained = toAny subscribed
551−#ifdef DEBUG_CYCLES
552− , eventSubscribedGetParents = do
553− s <- readIORef $ switchSubscribedCurrentParent subscribed
554− return [Some.This $ _eventSubscription_subscribed s]
555− , eventSubscribedHasOwnHeightRef = True
556− , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
557−#endif
558− }
559−
560−eventSubscribedCoincidence :: CoincidenceSubscribed x a -> EventSubscribed x
561−eventSubscribedCoincidence !subscribed = EventSubscribed
562− { eventSubscribedHeightRef = coincidenceSubscribedHeight subscribed
563− , eventSubscribedRetained = toAny subscribed
564−#ifdef DEBUG_CYCLES
565− , eventSubscribedGetParents = do
566− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
567− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
568− innerParents = maybeToList $ fmap Some.This innerSubscription
569− return $ outerParent : innerParents
570− , eventSubscribedHasOwnHeightRef = True
571− , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
572−#endif
573− }
574−
575−getEventSubscribedHeight :: EventSubscribed x -> IO Height
576−getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
577−
578−#ifdef DEBUG_CYCLES
579−whoCreatedEventSubscribed :: EventSubscribed x -> IO [String]
580−whoCreatedEventSubscribed = eventSubscribedWhoCreated
581−
582−walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
583−walkInvalidHeightParents s0 = do
584− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
585− h <- liftIO $ readIORef $ eventSubscribedHeightRef s
586− when (h == invalidHeight) $ do
587− when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
588− modify (Some.This s :)
589− mapM_ loop =<< liftIO (eventSubscribedGetParents s)
590− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
591− return subscribers
592−#endif
593−
594−{-# INLINE subscribeHoldEvent #-}
595−subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
596−subscribeHoldEvent = subscribeAndRead . holdEvent
597−
598−--------------------------------------------------------------------------------
599−-- Behavior
600−--------------------------------------------------------------------------------
601−
602−newtype Behavior x a = Behavior { readBehaviorTracked :: BehaviorM x a }
603−
604−behaviorHold :: Hold x p -> Behavior x (PatchTarget p)
605−behaviorHold !h = Behavior $ readHoldTracked h
606−
607−behaviorHoldIdentity :: Hold x (Identity a) -> Behavior x a
608−behaviorHoldIdentity = behaviorHold
609−
610−behaviorConst :: a -> Behavior x a
611−behaviorConst !a = Behavior $ return a
612−
613−behaviorPull :: Pull x a -> Behavior x a
614−behaviorPull !p = Behavior $ do
615− val <- liftIO $ readIORef $ pullValue p
616− case val of
617− Just subscribed -> do
618− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
619− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
620− liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
621− return $ pullSubscribedValue subscribed
622− Nothing -> do
623− i <- liftIO $ newInvalidatorPull p
624− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
625− parentsRef <- liftIO $ newIORef []
626− holdInits <- askBehaviorHoldInits
627− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
628− invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
629− parents <- liftIO $ readIORef parentsRef
630− let subscribed = PullSubscribed
631− { pullSubscribedValue = a
632− , pullSubscribedInvalidators = invsRef
633− , pullSubscribedOwnInvalidator = i
634− , pullSubscribedParents = parents
635− }
636− liftIO $ writeIORef (pullValue p) $ Just subscribed
637− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
638− return a
639−
640−behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
641−behaviorDyn !d = Behavior $ readHoldTracked =<< getDynHold d
642−
643−{-# INLINE readHoldTracked #-}
644−readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
645−readHoldTracked h = do
646− result <- liftIO $ readIORef $ holdValue h
647− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
648− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
649− return result
650−
651−{-# INLINABLE readBehaviorUntracked #-}
652−readBehaviorUntracked :: Defer (SomeHoldInit x) m => Behavior x a -> m a
653−readBehaviorUntracked b = do
654− holdInits <- getDeferralQueue
655− liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
656−
657−--------------------------------------------------------------------------------
658−-- Dynamic
659−--------------------------------------------------------------------------------
660−
661−data Dynamic x p = Dynamic
662− { dynamicCurrent :: !(Behavior x (PatchTarget p))
663− , dynamicUpdated :: !(Event x p)
664− }
665−
666−dynamicHold :: HasSpiderTimeline x => Hold x p -> Dynamic x p
667−dynamicHold !h = Dynamic
668− { dynamicCurrent = behaviorHold h
669− , dynamicUpdated = eventHold h
670− }
671−
672−dynamicHoldIdentity :: HasSpiderTimeline x => Hold x (Identity a) -> Dynamic x (Identity a)
673−dynamicHoldIdentity = dynamicHold
674−
675−dynamicConst :: PatchTarget p -> Dynamic x p
676−dynamicConst !a = Dynamic
677− { dynamicCurrent = behaviorConst a
678− , dynamicUpdated = eventNever
679− }
680−
681−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
682−dynamicDyn !d = Dynamic
683− { dynamicCurrent = behaviorDyn d
684− , dynamicUpdated = eventDyn d
685− }
686−
687−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
688−dynamicDynIdentity = dynamicDyn
689−
690−--------------------------------------------------------------------------------
691−-- Combinators
692−--------------------------------------------------------------------------------
693−
694−--TODO: Figure out why certain things are not 'representational', then make them
695−--representational so we can use coerce
69695
96+--TODO: Figure out why certain things are not 'representational', then make them representational so we can use coerce
69797--type role Hold representational
698−data Hold x p
699− = Hold { holdValue :: !(IORef (PatchTarget p))
700− , holdInvalidators :: !(IORef [Weak (Invalidator x)])
701− , holdEvent :: Event x p -- This must be lazy, or holds cannot be defined before their input Events
702− , holdParent :: !(IORef (Maybe (EventSubscription x))) -- Keeps its parent alive (will be undefined until the hold is initialized) --TODO: Probably shouldn't be an IORef
98+data Hold a
99+ = Hold { holdValue :: !(IORef a)
100+ , holdInvalidators :: !(IORef [Weak Invalidator])
101+ -- We need to use 'Any' for the next two things, because otherwise Hold inherits a nominal role for its 'a' parameter, and we want to be able to use 'coerce'
102+ , holdSubscriber :: !(IORef Any) -- Keeps its subscription alive; for some reason, a regular (or strict) reference to the Subscriber itself wasn't working, so had to use an IORef
103+ , holdParent :: !(IORef Any) -- Keeps its parent alive (will be undefined until the hold is initialized --TODO: Probably shouldn't be an IORef
703104#ifdef DEBUG_NODEIDS
704105 , holdNodeId :: Int
705106#endif
706107 }
707108
708−-- | A statically allocated 'SpiderTimeline'; this is the only instance of
709−-- 'SpiderTimeline' that can used when using
710−-- -fspecialize-to-spidertimeline-global.
711−data Global
712−
713−{-# NOINLINE globalSpiderTimelineEnv #-}
714−globalSpiderTimelineEnv :: SpiderTimelineEnv Global
715−globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
716−
717−-- | Stores all global data relevant to a particular Spider timeline; only one
718−-- value should exist for each type @x@
719−data SpiderTimelineEnv x = SpiderTimelineEnv
720− { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
721− , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
722−#ifdef DEBUG
723− , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
724−#endif
725− }
726−type role SpiderTimelineEnv nominal
727−
728−instance GEq SpiderTimelineEnv where
729− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
730− then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
731− else Nothing
732−
733−data EventEnv x
734− = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
735− , eventEnvHoldInits :: !(IORef [SomeHoldInit x]) -- Needed for Subscribe
736− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
737− , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
738− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
739− , eventEnvRootClears :: !(IORef [SomeRootClear])
740− , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
741− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
742− , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
109+data EventEnv
110+ = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment])
111+ , eventEnvHoldInits :: !(IORef [SomeHoldInit])
112+ , eventEnvClears :: !(IORef [SomeMaybeIORef])
113+ , eventEnvRootClears :: !(IORef [SomeDMapIORef])
114+ , eventEnvCurrentHeight :: !(IORef Int)
115+ , eventEnvCoincidenceInfos :: !(IORef [SomeCoincidenceInfo])
116+ , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
743117 }
744118
745−{-# INLINE runEventM #-}
746−runEventM :: EventM x a -> IO a
747−runEventM = unEventM
748−
749−asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
750−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
751−
752−class MonadIO m => Defer a m where
753− getDeferralQueue :: m (IORef [a])
754−
755−{-# INLINE defer #-}
756−defer :: Defer a m => a -> m ()
757−defer a = do
758− q <- getDeferralQueue
759− liftIO $ modifyIORef' q (a:)
760−
761−instance HasSpiderTimeline x => Defer (SomeAssignment x) (EventM x) where
762− {-# INLINE getDeferralQueue #-}
763− getDeferralQueue = asksEventEnv eventEnvAssignments
764−
765−instance HasSpiderTimeline x => Defer (SomeHoldInit x) (EventM x) where
766− {-# INLINE getDeferralQueue #-}
767− getDeferralQueue = asksEventEnv eventEnvHoldInits
768−
769−instance Defer (SomeHoldInit x) (BehaviorM x) where
770− {-# INLINE getDeferralQueue #-}
771− getDeferralQueue = BehaviorM $ asks snd
772−
773−instance HasSpiderTimeline x => Defer (SomeMergeUpdate x) (EventM x) where
774− {-# INLINE getDeferralQueue #-}
775− getDeferralQueue = asksEventEnv eventEnvMergeUpdates
776−
777−instance HasSpiderTimeline x => Defer (SomeMergeInit x) (EventM x) where
778− {-# INLINE getDeferralQueue #-}
779− getDeferralQueue = asksEventEnv eventEnvMergeInits
780−
781−class HasSpiderTimeline x => HasCurrentHeight x m | m -> x where
782− getCurrentHeight :: m Height
783− scheduleMerge :: Height -> EventM x () -> m ()
784−
785−instance HasSpiderTimeline x => HasCurrentHeight x (EventM x) where
786− {-# INLINE getCurrentHeight #-}
787− getCurrentHeight = do
788− heightRef <- asksEventEnv eventEnvCurrentHeight
789− liftIO $ readIORef heightRef
790− {-# INLINE scheduleMerge #-}
791− scheduleMerge height subscribed = do
792− delayedRef <- asksEventEnv eventEnvDelayedMerges
793− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [subscribed]
794−
795−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
796−class x ~ Global => HasSpiderTimeline x where
797−#else
798−class HasSpiderTimeline x where
799−#endif
800− -- | Retrieve the current SpiderTimelineEnv
801− spiderTimeline :: SpiderTimelineEnv x
119+runEventM :: EventM a -> EventEnv -> IO a
120+runEventM = runReaderT . unEventM
121+
122+askToAssignRef :: EventM (IORef [SomeAssignment])
123+askToAssignRef = EventM $ asks eventEnvAssignments
802124
803−instance HasSpiderTimeline Global where
804− spiderTimeline = globalSpiderTimelineEnv
125+askHoldInitRef :: EventM (IORef [SomeHoldInit])
126+askHoldInitRef = EventM $ asks eventEnvHoldInits
805127
806−putCurrentHeight :: HasSpiderTimeline x => Height -> EventM x ()
807−putCurrentHeight h = do
808− heightRef <- asksEventEnv eventEnvCurrentHeight
809− liftIO $ writeIORef heightRef $! h
128+getCurrentHeight :: EventM Int
129+getCurrentHeight = EventM $ do
130+ heightRef <- asks eventEnvCurrentHeight
131+ liftIO $ readIORef heightRef
810132
811−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
812− {-# INLINE getDeferralQueue #-}
813− getDeferralQueue = asksEventEnv eventEnvClears
133+putCurrentHeight :: Int -> EventM ()
134+putCurrentHeight h = EventM $ do
135+ heightRef <- asks eventEnvCurrentHeight
136+ liftIO $ writeIORef heightRef h
814137
815−{-# INLINE scheduleClear #-}
816−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
817−scheduleClear r = defer $ SomeClear r
138+scheduleClear :: IORef (Maybe a) -> EventM ()
139+scheduleClear r = EventM $ do
140+ clears <- asks eventEnvClears
141+ liftIO $ modifyIORef' clears (SomeMaybeIORef r :)
818142
819−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
820− {-# INLINE getDeferralQueue #-}
821− getDeferralQueue = asksEventEnv eventEnvRootClears
143+scheduleRootClear :: IORef (DMap k Identity) -> EventM ()
144+scheduleRootClear r = EventM $ do
145+ clears <- asks eventEnvRootClears
146+ liftIO $ modifyIORef' clears (SomeDMapIORef r :)
822147
823−{-# INLINE scheduleRootClear #-}
824−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
825−scheduleRootClear r = defer $ SomeRootClear r
148+scheduleMerge :: Int -> MergeSubscribed a -> EventM ()
149+scheduleMerge height subscribed = EventM $ do
150+ delayedRef <- asks eventEnvDelayedMerges
151+ liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) height [DelayedMerge subscribed]
826152
827−instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
828− {-# INLINE getDeferralQueue #-}
829− getDeferralQueue = asksEventEnv eventEnvResetCoincidences
153+emitCoincidenceInfo :: SomeCoincidenceInfo -> EventM ()
154+emitCoincidenceInfo sci = EventM $ do
155+ ciRef <- asks eventEnvCoincidenceInfos
156+ liftIO $ modifyIORef' ciRef (sci:)
830157
831158-- Note: hold cannot examine its event until after the phase is over
832−{-# INLINE [1] hold #-}
833−hold :: (Patch p, Defer (SomeHoldInit x) m) => PatchTarget p -> Event x p -> m (Hold x p)
159+hold :: a -> Event a -> EventM (Behavior a)
834160hold v0 e = do
835− valRef <- liftIO $ newIORef v0
836− invsRef <- liftIO $ newIORef []
837− parentRef <- liftIO $ newIORef Nothing
838−#ifdef DEBUG_NODEIDS
839− nodeId <- liftIO newNodeId
840−#endif
841− let h = Hold
842− { holdValue = valRef
843− , holdInvalidators = invsRef
844− , holdEvent = e
845− , holdParent = parentRef
161+ holdInitRef <- askHoldInitRef
162+ liftIO $ do
163+ valRef <- newIORef v0
164+ invsRef <- newIORef []
165+ parentRef <- newIORef $ error "hold not yet initialized (parent)"
166+ subscriberRef <- newIORef $ error "hold not yet initialized (subscriber)"
167+ let h = Hold
168+ { holdValue = valRef
169+ , holdInvalidators = invsRef
170+ , holdSubscriber = subscriberRef
171+ , holdParent = parentRef
846172#ifdef DEBUG_NODEIDS
847− , holdNodeId = nodeId
173+ , holdNodeId = unsafeNodeId (v0, e)
848174#endif
849− }
850− defer $ SomeHoldInit h
851− return h
852−
853−{-# INLINE getHoldEventSubscription #-}
854−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)
855−getHoldEventSubscription h = do
856− ep <- liftIO $ readIORef $ holdParent h
857− case ep of
858− Just subd -> return subd
859− Nothing -> do
860− let e = holdEvent h
861− subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
862− (subscription@(EventSubscription _ _), occ) <- subscribeAndRead e =<< liftIO (newSubscriberHold h)
863− liftIO $ writeIORef subscriptionRef $! subscription
864− case occ of
865− Nothing -> return ()
866− Just o -> do
867− old <- liftIO $ readIORef $ holdValue h
868− case apply o old of
869− Nothing -> return ()
870− Just new -> do
871− -- Need to evaluate these so that we don't retain the Hold itself
872− v <- liftIO $ evaluate $ holdValue h
873− i <- liftIO $ evaluate $ holdInvalidators h
874− defer $ SomeAssignment v i new
875− liftIO $ writeIORef (holdParent h) $ Just subscription
876− return subscription
877−
878−type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
175+ }
176+ s <- newSubscriberHold h
177+ writeIORef subscriberRef $ unsafeCoerce s
178+ modifyIORef' holdInitRef (SomeHoldInit e h :)
179+ return $ BehaviorHold h
180+
181+subscribeHold :: Event a -> Hold a -> EventM ()
182+subscribeHold e h = do
183+ toAssignRef <- askToAssignRef
184+ !s <- liftIO $ liftM unsafeCoerce $ readIORef $ holdSubscriber h -- This must be performed strictly so that the weak pointer points at the actual item
185+ ws <- liftIO $ mkWeakPtrWithDebug s "holdSubscriber"
186+ subd <- subscribe e $ WeakSubscriberSimple ws
187+ liftIO $ writeIORef (holdParent h) $ unsafeCoerce subd
188+ occ <- liftIO $ getEventSubscribedOcc subd
189+ case occ of
190+ Nothing -> return ()
191+ Just o -> liftIO $ modifyIORef' toAssignRef (SomeAssignment h o :)
879192
880193--type role BehaviorM representational
881194-- BehaviorM can sample behaviors
882−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
883−
884−instance Monad (BehaviorM x) where
885− {-# INLINE (>>=) #-}
886− BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
887− {-# INLINE (>>) #-}
888− BehaviorM x >> BehaviorM y = BehaviorM $ x >> y
889− {-# INLINE return #-}
890− return x = BehaviorM $ return x
891− {-# INLINE fail #-}
892− fail s = BehaviorM $ fail s
195+newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed])) IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
893196
894−data BehaviorSubscribed x a
895− = forall p. BehaviorSubscribedHold (Hold x p)
896− | BehaviorSubscribedPull (PullSubscribed x a)
197+data BehaviorSubscribed a
198+ = BehaviorSubscribedHold (Hold a)
199+ | BehaviorSubscribedPull (PullSubscribed a)
897200
898−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
201+data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
899202
900203--type role PullSubscribed representational
901−data PullSubscribed x a
204+data PullSubscribed a
902205 = PullSubscribed { pullSubscribedValue :: !a
903− , pullSubscribedInvalidators :: !(IORef [Weak (Invalidator x)])
904− , pullSubscribedOwnInvalidator :: !(Invalidator x)
905− , pullSubscribedParents :: ![SomeBehaviorSubscribed x] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
206+ , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
207+ , pullSubscribedOwnInvalidator :: !Invalidator
208+ , pullSubscribedParents :: ![SomeBehaviorSubscribed] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
906209 }
907210
908211--type role Pull representational
909−data Pull x a
910− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed x a)))
911− , pullCompute :: !(BehaviorM x a)
912−#ifdef DEBUG_NODEIDS
913− , pullNodeId :: Int
914−#endif
212+data Pull a
213+ = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
214+ , pullCompute :: !(BehaviorM a)
915215 }
916216
917−data Invalidator x
918− = forall a. InvalidatorPull (Pull x a)
919− | forall a. InvalidatorSwitch (SwitchSubscribed x a)
920−
921−data RootSubscribed x a = forall k. GCompare k => RootSubscribed
922− { rootSubscribedKey :: !(k a)
923− , rootSubscribedCachedSubscribed :: !(IORef (DMap k (RootSubscribed x))) -- From the original Root
924− , rootSubscribedSubscribers :: !(WeakBag (Subscriber x a))
925− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
926− , rootSubscribedUninit :: IO ()
927− , 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?
928−#ifdef DEBUG_NODEIDS
929− , rootSubscribedNodeId :: Int
930−#endif
931− }
217+data Invalidator
218+ = forall a. InvalidatorPull (Pull a)
219+ | forall a. InvalidatorSwitch (SwitchSubscribed a)
932220
933−data Root x (k :: * -> *)
221+data RootSubscribed a
222+ = RootSubscribed { rootSubscribedSubscribers :: !(IORef [WeakSubscriber a])
223+ , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
224+ }
225+
226+data Root (k :: * -> *)
934227 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
935− , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
936− , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
228+ , rootSubscribed :: !(IORef (DMap k RootSubscribed))
229+ , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
937230 }
938231
939−data SomeHoldInit x = forall p. Patch p => SomeHoldInit !(Hold x p)
940−
941−data SomeMergeUpdate x = SomeMergeUpdate
942− { _someMergeUpdate_update :: !(EventM x [EventSubscription x])
943− , _someMergeUpdate_invalidateHeight :: !(IO ())
944− , _someMergeUpdate_recalculateHeight :: !(IO ())
945− }
946−
947−newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
232+data SomeHoldInit = forall a. SomeHoldInit (Event a) (Hold a)
948233
949234-- EventM can do everything BehaviorM can, plus create holds
950−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
951−
952−newtype ComputeM x a = ComputeM (ReaderT (IORef [SomeHoldInit x]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
953−
954−instance Defer (SomeHoldInit x) (ComputeM x) where
955− getDeferralQueue = ComputeM ask
956−
957−{-# INLINABLE runComputeM #-}
958−runComputeM :: Defer (SomeHoldInit x) m => ComputeM x a -> m a
959−runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
960−
961−newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
962−
963−data MergeSubscribedParentWithMove x k a = MergeSubscribedParentWithMove
964− { _mergeSubscribedParentWithMove_subscription :: !(EventSubscription x)
965− , _mergeSubscribedParentWithMove_key :: !(IORef (k a))
966− }
967−
968−data HeightBag = HeightBag
969− { _heightBag_size :: {-# UNPACK #-} !Int
970− , _heightBag_contents :: !(IntMap Word) -- Number of excess in each bucket
971− }
972− deriving (Show, Read, Eq, Ord)
973−
974−heightBagEmpty :: HeightBag
975−heightBagEmpty = heightBagVerify $ HeightBag 0 IntMap.empty
976−
977−heightBagSize :: HeightBag -> Int
978−heightBagSize = _heightBag_size
979−
980−heightBagFromList :: [Height] -> HeightBag
981−heightBagFromList heights = heightBagVerify $ foldl' (flip heightBagAdd) heightBagEmpty heights
982−
983−heightBagAdd :: Height -> HeightBag -> HeightBag
984−heightBagAdd (Height h) (HeightBag s c) = heightBagVerify $ HeightBag (succ s) $ IntMap.insertWithKey (\_ _ old -> succ old) h 0 c
985−
986−heightBagRemove :: Height -> HeightBag -> HeightBag
987−heightBagRemove (Height h) b@(HeightBag s c) = heightBagVerify $ case IntMap.lookup h c of
988− Nothing -> error $ "heightBagRemove: Height " <> show h <> " not present in bag " <> show b
989− Just old -> HeightBag (pred s) $ case old of
990− 0 -> IntMap.delete h c
991− _ -> IntMap.insert h (pred old) c
235+newtype EventM a = EventM { unEventM :: ReaderT EventEnv IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, 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
236+
237+data PushSubscribed a b
238+ = PushSubscribed { pushSubscribedOccurrence :: !(IORef (Maybe b)) -- If the current height is less than our height, this should always be Nothing; during our height, this will get filled in at some point, always before our children are notified; after our height, this will be filled in with the correct value (Nothing if we are not firing, Just if we are)
239+ , pushSubscribedHeight :: !(IORef Int)
240+ , pushSubscribedSubscribers :: !(IORef [WeakSubscriber b])
241+ , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die
242+ , pushSubscribedParent :: !(EventSubscribed a)
243+#ifdef DEBUG_NODEIDS
244+ , pushSubscribedNodeId :: Int
245+#endif
246+ }
992247
993−heightBagMax :: HeightBag -> Height
994−heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
995− Just ((h, _), _) -> Height h
996− Nothing -> zeroHeight
248+data Push a b
249+ = Push { pushCompute :: !(a -> EventM (Maybe b)) -- Compute the current firing value; assumes that its parent has been computed
250+ , pushParent :: !(Event a)
251+ , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
252+ }
997253
998−heightBagVerify :: HeightBag -> HeightBag
999−#ifdef DEBUG
1000−heightBagVerify b@(HeightBag s c) = if
1001− | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
1002− -> error $ "heightBagVerify: size doesn't match: " <> show b
1003− | unHeight invalidHeight `IntMap.member` c
1004− -> error $ "heightBagVerify: contains invalid height: " <> show b
1005− | otherwise -> b
1006−#else
1007−heightBagVerify = id
254+data MergeSubscribed k
255+ = MergeSubscribed { mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
256+ , mergeSubscribedAccum :: !(IORef (DMap k Identity)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
257+ , mergeSubscribedHeight :: !(IORef Int)
258+ , mergeSubscribedSubscribers :: !(IORef [WeakSubscriber (DMap k Identity)])
259+ , mergeSubscribedSelf :: !Any -- Hold all our Subscribers in memory
260+ , mergeSubscribedParents :: !(DMap k EventSubscribed)
261+#ifdef DEBUG_NODEIDS
262+ , mergeSubscribedNodeId :: Int
1008263#endif
264+ }
1009265
1010−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1011− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1012− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1013− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1014− }
1015−
1016−data FanSubscribed (x :: *) k
1017− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1018− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1019− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1020− , fanSubscribedParent :: !(EventSubscription x)
266+--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
267+data Merge k
268+ = Merge { mergeParents :: !(DMap k Event)
269+ , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k))) --TODO: Can we replace this with an unsafePerformIO thunk?
270+ }
271+
272+data FanSubscriberKey k a where
273+ FanSubscriberKey :: k a -> FanSubscriberKey k [WeakSubscriber a]
274+
275+instance GEq k => GEq (FanSubscriberKey k) where
276+ geq (FanSubscriberKey a) (FanSubscriberKey b) = case geq a b of
277+ Nothing -> Nothing
278+ Just Refl -> Just Refl
279+
280+instance GCompare k => GCompare (FanSubscriberKey k) where
281+ gcompare (FanSubscriberKey a) (FanSubscriberKey b) = case gcompare a b of
282+ GLT -> GLT
283+ GEQ -> GEQ
284+ GGT -> GGT
285+
286+data FanSubscribed k
287+ = FanSubscribed { fanSubscribedSubscribers :: !(IORef (DMap (FanSubscriberKey k) Identity))
288+ , fanSubscribedParent :: !(EventSubscribed (DMap k Identity))
289+ , fanSubscribedSelf :: {-# NOUNPACK #-} (Subscriber (DMap k Identity))
1021290#ifdef DEBUG_NODEIDS
1022291 , fanSubscribedNodeId :: Int
1023292#endif
1024293 }
1025294
1026−data Fan x k
1027− = Fan { fanParent :: !(Event x (DMap k Identity))
1028− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
295+data Fan k
296+ = Fan { fanParent :: !(Event (DMap k Identity))
297+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
1029298 }
1030299
1031−data SwitchSubscribed x a
1032− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1033− , switchSubscribedOccurrence :: !(IORef (Maybe a))
1034− , switchSubscribedHeight :: !(IORef Height)
1035− , switchSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1036− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !(Invalidator x)
1037− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak (Invalidator x)))
1038− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed x])
1039− , switchSubscribedParent :: !(Behavior x (Event x a))
1040− , switchSubscribedCurrentParent :: !(IORef (EventSubscription x))
1041− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed x a)))
300+data SwitchSubscribed a
301+ = SwitchSubscribed { switchSubscribedOccurrence :: !(IORef (Maybe a))
302+ , switchSubscribedHeight :: !(IORef Int)
303+ , switchSubscribedSubscribers :: !(IORef [WeakSubscriber a])
304+ , switchSubscribedSelf :: {-# NOUNPACK #-} (Subscriber a)
305+ , switchSubscribedSelfWeak :: !(IORef (Weak (Subscriber a)))
306+ , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} Invalidator
307+ , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
308+ , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
309+ , switchSubscribedParent :: !(Behavior (Event a))
310+ , switchSubscribedCurrentParent :: !(IORef (EventSubscribed a))
1042311#ifdef DEBUG_NODEIDS
1043312 , switchSubscribedNodeId :: Int
1044313#endif
1045314 }
1046315
1047−data Switch x a
1048− = Switch { switchParent :: !(Behavior x (Event x a))
1049− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
316+data Switch a
317+ = Switch { switchParent :: !(Behavior (Event a))
318+ , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
1050319 }
1051320
1052−#ifdef USE_TEMPLATE_HASKELL
1053−{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
1054−#endif
1055−data CoincidenceSubscribed x a
1056− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1057− , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
1058− , coincidenceSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1059− , coincidenceSubscribedHeight :: !(IORef Height)
1060− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber x (Event x a))
1061− , coincidenceSubscribedOuterParent :: !(EventSubscription x)
1062− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed x)))
1063− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed x a)))
321+data CoincidenceSubscribed a
322+ = CoincidenceSubscribed { coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
323+ , coincidenceSubscribedSubscribers :: !(IORef [WeakSubscriber a])
324+ , coincidenceSubscribedHeight :: !(IORef Int)
325+ , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
326+ , coincidenceSubscribedOuterParent :: !(EventSubscribed (Event a))
327+ , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
1064328#ifdef DEBUG_NODEIDS
1065329 , coincidenceSubscribedNodeId :: Int
1066330#endif
1067331 }
1068332
1069−data Coincidence x a
1070− = Coincidence { coincidenceParent :: !(Event x (Event x a))
1071− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
333+data Coincidence a
334+ = Coincidence { coincidenceParent :: !(Event (Event a))
335+ , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
1072336 }
1073337
338+data Box a = Box { unBox :: a }
339+
340+--type WeakSubscriber a = Weak (Subscriber a)
341+data WeakSubscriber a
342+ = forall k. GCompare k => WeakSubscriberMerge !(k a) !(Weak (Box (MergeSubscribed k))) --TODO: Can we inline the GCompare?
343+ | WeakSubscriberSimple !(Weak (Subscriber a))
344+
345+showWeakSubscriberType :: WeakSubscriber a -> String
346+showWeakSubscriberType = \case
347+ WeakSubscriberMerge _ _ -> "WeakSubscriberMerge"
348+ WeakSubscriberSimple _ -> "WeakSubscriberSimple"
349+
350+deRefWeakSubscriber :: WeakSubscriber a -> IO (Maybe (Subscriber a))
351+deRefWeakSubscriber ws = case ws of
352+ WeakSubscriberSimple w -> deRefWeak w
353+ WeakSubscriberMerge k w -> liftM (fmap $ SubscriberMerge k . unBox) $ deRefWeak w
354+
355+data Subscriber a
356+ = forall b. SubscriberPush !(a -> EventM (Maybe b)) (PushSubscribed a b)
357+ | forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k) --TODO: Can we inline the GCompare?
358+ | forall k. (GCompare k, a ~ DMap k Identity) => SubscriberFan (FanSubscribed k)
359+ | SubscriberHold !(Hold a)
360+ | SubscriberSwitch (SwitchSubscribed a)
361+ | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
362+ | SubscriberCoincidenceInner (CoincidenceSubscribed a)
363+
364+showSubscriberType :: Subscriber a -> String
365+showSubscriberType = \case
366+ SubscriberPush _ _ -> "SubscriberPush"
367+ SubscriberMerge _ _ -> "SubscriberMerge"
368+ SubscriberFan _ -> "SubscriberFan"
369+ SubscriberHold _ -> "SubscriberHold"
370+ SubscriberSwitch _ -> "SubscriberSwitch"
371+ SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
372+ SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
373+
374+data Event a
375+ = forall k. GCompare k => EventRoot !(k a) !(Root k)
376+ | EventNever
377+ | forall b. EventPush !(Push b a)
378+ | forall k. (GCompare k, a ~ DMap k Identity) => EventMerge !(Merge k)
379+ | forall k. GCompare k => EventFan !(k a) !(Fan k)
380+ | EventSwitch !(Switch a)
381+ | EventCoincidence !(Coincidence a)
382+
383+showEventType :: Event a -> String
384+showEventType = \case
385+ EventRoot _ _ -> "EventRoot"
386+ EventNever -> "EventNever"
387+ EventPush _ -> "EventPush"
388+ EventMerge _ -> "EventMerge"
389+ EventFan _ _ -> "EventFan"
390+ EventSwitch _ -> "EventSwitch"
391+ EventCoincidence _ -> "EventCoincidence"
392+
393+data EventSubscribed a
394+ = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
395+ | EventSubscribedNever
396+ | forall b. EventSubscribedPush !(PushSubscribed b a)
397+ | forall k. (GCompare k, a ~ DMap k Identity) => EventSubscribedMerge !(MergeSubscribed k)
398+ | forall k. GCompare k => EventSubscribedFan !(k a) !(FanSubscribed k)
399+ | EventSubscribedSwitch !(SwitchSubscribed a)
400+ | EventSubscribedCoincidence !(CoincidenceSubscribed a)
401+
402+-- These function are constructor functions that are marked NOINLINE so they are
403+-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
404+-- so any weak references that are attached to the constructors will have their
405+-- finalizer run. Using the opaque constructor, does not see the
406+-- constructor application, so it behaves like an IORef and cannot be fused away.
407+--
408+-- The result is also evaluated to WHNF, since forcing a thunk invalidates
409+-- the weak pointer to it in some cases.
410+
411+{-# NOINLINE newRootSubscribed #-}
412+newRootSubscribed :: IO (Maybe a) -> IORef [WeakSubscriber a] -> IO (RootSubscribed a)
413+newRootSubscribed occ subs =
414+ return $! RootSubscribed
415+ { rootSubscribedOccurrence = occ
416+ , rootSubscribedSubscribers = subs
417+ }
418+
419+{-# NOINLINE newSubscriberPush #-}
420+newSubscriberPush :: (a -> EventM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
421+newSubscriberPush compute subd = return $! SubscriberPush compute subd
422+
423+{-# NOINLINE newSubscriberHold #-}
424+newSubscriberHold :: Hold a -> IO (Subscriber a)
425+newSubscriberHold h = return $! SubscriberHold h
426+
427+{-# NOINLINE newSubscriberFan #-}
428+newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
429+newSubscriberFan subd = return $! SubscriberFan subd
430+
431+{-# NOINLINE newSubscriberSwitch #-}
432+newSubscriberSwitch :: SwitchSubscribed a -> IO (Subscriber a)
433+newSubscriberSwitch subd = return $! SubscriberSwitch subd
434+
435+{-# NOINLINE newSubscriberCoincidenceOuter #-}
436+newSubscriberCoincidenceOuter :: CoincidenceSubscribed b -> IO (Subscriber (Event b))
437+newSubscriberCoincidenceOuter subd = return $! SubscriberCoincidenceOuter subd
438+
439+{-# NOINLINE newSubscriberCoincidenceInner #-}
440+newSubscriberCoincidenceInner :: CoincidenceSubscribed a -> IO (Subscriber a)
441+newSubscriberCoincidenceInner subd = return $! SubscriberCoincidenceInner subd
442+
1074443{-# NOINLINE newInvalidatorSwitch #-}
1075−newInvalidatorSwitch :: SwitchSubscribed x a -> IO (Invalidator x)
444+newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
1076445newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1077446
1078447{-# NOINLINE newInvalidatorPull #-}
1079−newInvalidatorPull :: Pull x a -> IO (Invalidator x)
448+newInvalidatorPull :: Pull a -> IO Invalidator
1080449newInvalidatorPull p = return $! InvalidatorPull p
1081450
1082−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1083− fmapMaybe f = push $ return . f
1084−
1085−instance HasSpiderTimeline x => Align (Event x) where
1086− nil = eventNever
1087− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1088−
1089−newtype Dyn x p = Dyn { unDyn :: IORef (Either (BehaviorM x (PatchTarget p), Event x p) (Hold x p)) }
1090−
1091−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1092−newMapDyn f d = dynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1093−
1094−--TODO: Avoid the duplication between this and R.zipDynWith
1095−zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
1096−zipDynWith f da db =
1097− let eab = align (dynamicUpdated da) (dynamicUpdated db)
1098− ec = flip push eab $ \o -> do
1099− (a, b) <- case o of
1100− This (Identity a) -> do
1101− b <- readBehaviorUntracked $ dynamicCurrent db
1102− return (a, b)
1103− That (Identity b) -> do
1104− a <- readBehaviorUntracked $ dynamicCurrent da
1105− return (a, b)
1106− These (Identity a) (Identity b) -> return (a, b)
1107− return $ Just $ Identity $ f a b
1108− in dynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (dynamicCurrent da) <*> readBehaviorUntracked (dynamicCurrent db)) ec
1109−
1110−unsafeDyn :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1111−unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
1112− where x = (readV0, v')
451+{-# NOINLINE newBox #-}
452+newBox :: a -> IO (Box a)
453+newBox a = return $! Box a
454+
455+--type role Behavior representational
456+data Behavior a
457+ = BehaviorHold !(Hold a)
458+ | BehaviorConst !a
459+ | BehaviorPull !(Pull a)
1113460
1114461-- ResultM can read behaviors and events
1115462type ResultM = EventM
@@ -1118,410 +465,566 @@ type ResultM = EventM
1118465unsafeNewIORef :: a -> b -> IORef b
1119466unsafeNewIORef _ b = unsafePerformIO $ newIORef b
1120467
1121−instance HasSpiderTimeline x => Functor (Event x) where
468+instance Functor Event where
1122469 fmap f = push $ return . Just . f
1123470
1124−instance Functor (Behavior x) where
1125− fmap f = pull . fmap f . readBehaviorTracked
471+instance Functor Behavior where
472+ fmap f = pull . liftM f . readBehaviorTracked
1126473
1127−{-# INLINE push #-}
1128−push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
1129−push f e = cacheEvent (pushCheap f e)
474+{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
475+push :: (a -> EventM (Maybe b)) -> Event a -> Event b
476+push f e = EventPush $ Push
477+ { pushCompute = f
478+ , pushParent = e
479+ , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
480+ }
481+{-# RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
1130482
1131−{-# INLINABLE pull #-}
1132−pull :: BehaviorM x a -> Behavior x a
1133−pull a = behaviorPull $ Pull
483+{-# NOINLINE pull #-}
484+pull :: BehaviorM a -> Behavior a
485+pull a = BehaviorPull $ Pull
1134486 { pullCompute = a
1135487 , pullValue = unsafeNewIORef a Nothing
1136−#ifdef DEBUG_NODEIDS
1137− , pullNodeId = unsafeNodeId a
1138−#endif
1139488 }
489+{-# RULES "pull/pull" forall a. pull (readBehaviorTracked (pull a)) = pull a #-}
1140490
1141−{-# INLINABLE switch #-}
1142−switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1143−switch a = eventSwitch $ Switch
491+{-# NOINLINE switch #-}
492+switch :: Behavior (Event a) -> Event a
493+switch a = EventSwitch $ Switch
1144494 { switchParent = a
1145495 , switchSubscribed = unsafeNewIORef a Nothing
1146496 }
497+{-# RULES "switch/constB" forall e. switch (BehaviorConst e) = e #-}
1147498
1148−coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1149−coincidence a = eventCoincidence $ Coincidence
499+coincidence :: Event (Event a) -> Event a
500+coincidence a = EventCoincidence $ Coincidence
1150501 { coincidenceParent = a
1151502 , coincidenceSubscribed = unsafeNewIORef a Nothing
1152503 }
1153504
505+newRoot :: IO (Root k)
506+newRoot = do
507+ occRef <- newIORef DMap.empty
508+ subscribedRef <- newIORef DMap.empty
509+ return $ Root
510+ { rootOccurrence = occRef
511+ , rootSubscribed = subscribedRef
512+ , rootInit = \_ _ -> return $ return ()
513+ }
514+
515+propagateAndUpdateSubscribersRef :: IORef [WeakSubscriber a] -> a -> EventM ()
516+propagateAndUpdateSubscribersRef subscribersRef a = do
517+ subscribers <- liftIO $ readIORef subscribersRef
518+ liftIO $ writeIORef subscribersRef []
519+ stillAlive <- propagate a subscribers
520+ liftIO $ modifyIORef' subscribersRef (++stillAlive)
521+
1154522-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1155−run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
523+run :: [DSum RootTrigger Identity] -> ResultM b -> IO b
1156524run roots after = do
1157− tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1158− t <- SpiderHost ask
1159− result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock t) $ \_ -> flip runReaderT t $ unSpiderHost $ runFrame $ do
525+ when debugPropagate $ putStrLn "Running an event frame"
526+ result <- runFrame $ do
1160527 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1161528 occBefore <- liftIO $ do
1162529 occBefore <- readIORef occRef
1163− writeIORef occRef $! DMap.insert k a occBefore
530+ writeIORef occRef $ DMap.insert k a occBefore
1164531 return occBefore
1165532 if DMap.null occBefore
1166533 then do scheduleRootClear occRef
1167534 return $ Just r
1168535 else return Nothing
1169536 forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
1170− propagate a subscribersRef
1171− delayedRef <- asksEventEnv eventEnvDelayedMerges
537+ propagateAndUpdateSubscribersRef subscribersRef a
538+ delayedRef <- EventM $ asks eventEnvDelayedMerges
1172539 let go = do
1173540 delayed <- liftIO $ readIORef delayedRef
1174541 case IntMap.minViewWithKey delayed of
1175542 Nothing -> return ()
1176− Just ((currentHeight, cur), future) -> do
1177− tracePropagate (Proxy :: Proxy x) $ "Running height " ++ show currentHeight
1178− putCurrentHeight $ Height currentHeight
1179− liftIO $ writeIORef delayedRef $! future
1180− sequence_ cur
543+ Just ((currentHeight, current), future) -> do
544+ when debugPropagate $ liftIO $ putStrLn $ "Running height " ++ show currentHeight
545+ putCurrentHeight currentHeight
546+ liftIO $ writeIORef delayedRef future
547+ forM_ current $ \d -> case d of
548+ DelayedMerge subscribed -> do
549+ height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
550+ case height `compare` currentHeight of
551+ LT -> error "Somehow a merge's height has been decreased after it was scheduled"
552+ GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
553+ EQ -> do
554+ m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
555+ liftIO $ writeIORef (mergeSubscribedAccum subscribed) DMap.empty
556+ --TODO: Assert that m is not empty
557+ liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
558+ scheduleClear $ mergeSubscribedOccurrence subscribed
559+ propagateAndUpdateSubscribersRef (mergeSubscribedSubscribers subscribed) m
1181560 go
1182561 go
1183562 putCurrentHeight maxBound
1184563 after
1185− tracePropagate (Proxy :: Proxy x) "Done running an event frame"
564+ when debugPropagate $ putStrLn "Done running an event frame"
1186565 return result
1187566
1188−scheduleMerge' :: HasSpiderTimeline x => Height -> IORef Height -> EventM x () -> EventM x ()
1189−scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
1190− height <- liftIO $ readIORef heightRef
1191− currentHeight <- getCurrentHeight
1192− case height `compare` currentHeight of
1193− LT -> error "Somehow a merge's height has been decreased after it was scheduled"
1194− GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1195− EQ -> a
567+data SomeMaybeIORef = forall a. SomeMaybeIORef (IORef (Maybe a))
1196568
1197−data SomeClear = forall a. SomeClear (IORef (Maybe a))
569+data SomeDMapIORef = forall k. SomeDMapIORef (IORef (DMap k Identity))
1198570
1199−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
571+data SomeAssignment = forall a. SomeAssignment (Hold a) a
1200572
1201−data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
573+data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
1202574
1203575debugFinalize :: Bool
1204576debugFinalize = False
1205577
1206578mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
1207−mkWeakPtrWithDebug x debugNote = do
1208− x' <- evaluate x
1209− mkWeakPtr x' $
1210− if debugFinalize
1211− then Just $ putStrLn $ "finalizing: " ++ debugNote
1212− else Nothing
579+mkWeakPtrWithDebug x debugNote = mkWeakPtr x $
580+ if debugFinalize
581+ then Just $ putStrLn $ "finalizing: " ++ debugNote
582+ else Nothing
1213583
1214584type WeakList a = [Weak a]
1215585
1216−{-# INLINE withIncreasedDepth #-}
1217−#ifdef DEBUG
1218−withIncreasedDepth :: CanTrace x m => m a -> m a
1219−withIncreasedDepth a = do
1220− spiderTimeline <- askSpiderTimelineEnv
1221− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1222− result <- a
1223− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1224− return result
1225−#else
1226−withIncreasedDepth :: m a -> m a
1227−withIncreasedDepth = id
1228−#endif
586+--TODO: Is it faster to clean up every time, or to occasionally go through and clean up as needed?
587+traverseAndCleanWeakList_ :: Monad m => (wa -> m (Maybe a)) -> [wa] -> (a -> m ()) -> m [wa]
588+traverseAndCleanWeakList_ deRef ws f = go ws
589+ where go [] = return []
590+ go (h:t) = do
591+ ma <- deRef h
592+ case ma of
593+ Just a -> do
594+ f a
595+ t' <- go t
596+ return $ h : t'
597+ Nothing -> go t
598+
599+-- | Propagate everything at the current height
600+propagate :: a -> [WeakSubscriber a] -> EventM [WeakSubscriber a]
601+propagate a subscribers = do
602+ traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subscribers $ \s -> case s of
603+ SubscriberPush compute subscribed -> do
604+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberPush" <> showNodeId subscribed
605+ occ <- compute a
606+ case occ of
607+ Nothing -> return () -- No need to write a Nothing back into the Ref
608+ Just o -> do
609+ liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
610+ scheduleClear $ pushSubscribedOccurrence subscribed
611+ liftIO . writeIORef (pushSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (pushSubscribedSubscribers subscribed))
612+ SubscriberMerge k subscribed -> do
613+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberMerge" <> showNodeId subscribed
614+ oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
615+ liftIO $ writeIORef (mergeSubscribedAccum subscribed) $ DMap.insertWith (error "Same key fired multiple times for") k (Identity a) oldM
616+ when (DMap.null oldM) $ do -- Only schedule the firing once
617+ height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
618+ --TODO: assertions about height
619+ currentHeight <- getCurrentHeight
620+ when (height <= currentHeight) $ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")"
621+ scheduleMerge height subscribed
622+ SubscriberFan subscribed -> do
623+ subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
624+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
625+ --TODO: We need a better DMap intersection; here, we are assuming that the number of firing keys is small and the number of subscribers is large
626+ forM_ (DMap.toList a) $ \(k :=> Identity v) -> case DMap.lookup (FanSubscriberKey k) subs of
627+ Nothing -> do
628+ when debugPropagate $ liftIO $ putStrLn "No subscriber for key"
629+ return ()
630+ Just (Identity subsubs) -> do
631+ _ <- propagate v subsubs --TODO: use the value of this
632+ return ()
633+ --TODO: The following is way too slow to do all the time
634+ subs' <- liftIO $ forM (DMap.toList subs) $ ((\(FanSubscriberKey k :=> Identity subsubs) -> do
635+ subsubs' <- traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subsubs (const $ return ())
636+ return $ if null subsubs'
637+ then Nothing
638+ else Just $ FanSubscriberKey k :=> Identity subsubs') :: DSum (FanSubscriberKey k) Identity -> IO (Maybe (DSum (FanSubscriberKey k) Identity)))
639+ liftIO $ writeIORef (fanSubscribedSubscribers subscribed) $ DMap.fromDistinctAscList $ catMaybes subs'
640+ SubscriberHold h -> do
641+ invalidators <- liftIO $ readIORef $ holdInvalidators h
642+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
643+ toAssignRef <- askToAssignRef
644+ liftIO $ modifyIORef' toAssignRef (SomeAssignment h a :)
645+ SubscriberSwitch subscribed -> do
646+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberSwitch" <> showNodeId subscribed
647+ liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
648+ scheduleClear $ switchSubscribedOccurrence subscribed
649+ subs <- liftIO $ readIORef $ switchSubscribedSubscribers subscribed
650+ liftIO . writeIORef (switchSubscribedSubscribers subscribed) =<< propagate a subs
651+ SubscriberCoincidenceOuter subscribed -> do
652+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
653+ outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
654+ when debugPropagate $ liftIO $ putStrLn $ " outerHeight = " <> show outerHeight
655+ (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
656+ when debugPropagate $ liftIO $ putStrLn $ " isJust occ = " <> show (isJust occ)
657+ when debugPropagate $ liftIO $ putStrLn $ " innerHeight = " <> show innerHeight
658+ liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
659+ scheduleClear $ coincidenceSubscribedInnerParent subscribed
660+ case occ of
661+ Nothing -> do
662+ when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
663+ writeIORef (coincidenceSubscribedHeight subscribed) innerHeight
664+ mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
665+ mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
666+ Just o -> do -- Since it's already firing, no need to adjust height
667+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
668+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
669+ liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
670+ SubscriberCoincidenceInner subscribed -> do
671+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceInner" <> showNodeId subscribed
672+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
673+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
674+ liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate a =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
1229675
1230−type CanTrace x m = (HasSpiderTimeline x, MonadIO m)
676+data SomeCoincidenceInfo = forall a. SomeCoincidenceInfo (Weak (Subscriber a)) (Subscriber a) (Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1231677
1232−{-# INLINE tracePropagate #-}
1233−tracePropagate :: (CanTrace x m) => proxy x -> String -> m ()
1234−tracePropagate p = traceWhen p debugPropagate
678+subscribeCoincidenceInner :: Event a -> Int -> CoincidenceSubscribed a -> EventM (Maybe a, Int, EventSubscribed a)
679+subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
680+ subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
681+ wsubInner <- liftIO $ mkWeakPtrWithDebug subInner "SubscriberCoincidenceInner"
682+ innerSubd <- {-# SCC "innerSubd" #-} (subscribe o $ WeakSubscriberSimple wsubInner)
683+ innerOcc <- liftIO $ getEventSubscribedOcc innerSubd
684+ innerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef innerSubd
685+ let height = max innerHeight outerHeight
686+ emitCoincidenceInfo $ SomeCoincidenceInfo wsubInner subInner $ if height > outerHeight then Just subscribedUnsafe else Nothing
687+ return (innerOcc, height, innerSubd)
1235688
1236−{-# INLINE traceInvalidate #-}
1237−traceInvalidate :: String -> IO ()
1238−traceInvalidate = when debugInvalidate . liftIO . putStrLn
689+readBehavior :: Behavior a -> IO a
690+readBehavior b = runBehaviorM (readBehaviorTracked b) Nothing --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
1239691
1240−{-# INLINE traceWhen #-}
1241−traceWhen :: (CanTrace x m) => proxy x -> Bool -> String -> m ()
1242−traceWhen p b message = traceMWhen p b $ return message
692+runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IO a
693+runBehaviorM a mwi = runReaderT (unBehaviorM a) mwi
1243694
1244−{-# INLINE traceMWhen #-}
1245−traceMWhen :: (CanTrace x m) => proxy x -> Bool -> m String -> m ()
1246−traceMWhen _ b getMessage = when b $ do
1247− message <- getMessage
1248−#ifdef DEBUG
1249− spiderTimeline <- askSpiderTimelineEnv
1250− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1251−#else
1252− let d = 0
1253−#endif
1254− liftIO $ putStrLn $ replicate d ' ' <> message
1255−
1256−whoCreatedIORef :: IORef a -> IO [String]
1257−whoCreatedIORef (IORef a) = whoCreated $! a
1258−
1259−#ifdef DEBUG_CYCLES
1260−groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
1261−groupByHead = \case
1262− [] -> []
1263− (x :| xs) : t -> case groupByHead t of
1264− [] -> [(x, xs :| [])]
1265− l@((y, yss) : t')
1266− | x == y -> (x, xs `NonEmpty.cons` yss) : t'
1267− | otherwise -> (x, xs :| []) : l
1268−
1269−listsToForest :: Eq a => [[a]] -> Forest a
1270−listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1271−#endif
695+askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
696+askInvalidator = liftM (fmap fst) $ BehaviorM ask
1272697
1273−{-# INLINE propagateSubscriberHold #-}
1274−propagateSubscriberHold :: forall x p. (HasSpiderTimeline x, Patch p) => Hold x p -> p -> EventM x ()
1275−propagateSubscriberHold h a = do
1276− {-# SCC "trace" #-} traceMWhen (Proxy :: Proxy x) debugPropagate $ liftIO $ do
1277− invalidators <- liftIO $ readIORef $ holdInvalidators h
1278− return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1279− v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1280− case {-# SCC "apply" #-} apply a v of
1281− Nothing -> return ()
1282− Just v' -> do
1283− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate (Proxy :: Proxy x) $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1284− vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1285− iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1286− defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1287−
1288−data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1289−
1290−runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1291−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1292−
1293−askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1294−askInvalidator = do
1295− (!m, _) <- BehaviorM ask
1296− case m of
1297− Nothing -> return Nothing
1298− Just (!wi, _) -> return $ Just wi
1299−
1300−askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1301−askParentsRef = do
1302− (!m, _) <- BehaviorM ask
1303− case m of
1304− Nothing -> return Nothing
1305− Just (_, !p) -> return $ Just p
1306−
1307−askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1308−askBehaviorHoldInits = do
1309− (_, !his) <- BehaviorM ask
1310− return his
1311−
1312−{-# INLINE getDynHold #-}
1313−getDynHold :: (Defer (SomeHoldInit x) m, Patch p) => Dyn x p -> m (Hold x p)
1314−getDynHold d = do
1315− mh <- liftIO $ readIORef $ unDyn d
1316− case mh of
1317− Right h -> return h
1318− Left (readV0, v') -> do
1319− holdInits <- getDeferralQueue
1320− v0 <- liftIO $ runBehaviorM readV0 Nothing holdInits
1321− h <- hold v0 v'
1322− liftIO $ writeIORef (unDyn d) $ Right h
1323− return h
698+askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
699+askParentsRef = liftM (fmap snd) $ BehaviorM ask
700+
701+readBehaviorTracked :: Behavior a -> BehaviorM a
702+readBehaviorTracked b = case b of
703+ BehaviorHold h -> do
704+ result <- liftIO $ readIORef $ holdValue h
705+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
706+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
707+ liftIO $ touch $ holdSubscriber h
708+ return result
709+ BehaviorConst a -> return a
710+ BehaviorPull p -> do
711+ val <- liftIO $ readIORef $ pullValue p
712+ case val of
713+ Just subscribed -> do
714+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
715+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
716+ liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
717+ return $ pullSubscribedValue subscribed
718+ Nothing -> do
719+ i <- liftIO $ newInvalidatorPull p
720+ wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
721+ parentsRef <- liftIO $ newIORef []
722+ a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
723+ invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
724+ parents <- liftIO $ readIORef parentsRef
725+ let subscribed = PullSubscribed
726+ { pullSubscribedValue = a
727+ , pullSubscribedInvalidators = invsRef
728+ , pullSubscribedOwnInvalidator = i
729+ , pullSubscribedParents = parents
730+ }
731+ liftIO $ writeIORef (pullValue p) $ Just subscribed
732+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
733+ return a
734+
735+readEvent :: Event a -> ResultM (Maybe a)
736+readEvent e = case e of
737+ EventRoot k r -> liftIO . liftM (coerce . DMap.lookup k) . readIORef $ rootOccurrence r
738+ EventNever -> return Nothing
739+ EventPush p -> do
740+ subscribed <- getPushSubscribed p
741+ liftIO $ do
742+ result <- readIORef $ pushSubscribedOccurrence subscribed -- Since ResultM is always called after the final height is reached, this will always be valid
743+ touch $ pushSubscribedSelf subscribed
744+ return result
745+ EventMerge m -> do
746+ subscribed <- getMergeSubscribed m
747+ liftIO $ do
748+ result <- readIORef $ mergeSubscribedOccurrence subscribed
749+ touch $ mergeSubscribedSelf subscribed
750+ return result
751+ EventFan k f -> do
752+ parentOcc <- readEvent $ fanParent f
753+ return . coerce $ DMap.lookup k =<< parentOcc
754+ EventSwitch s -> do
755+ subscribed <- getSwitchSubscribed s
756+ liftIO $ do
757+ result <- readIORef $ switchSubscribedOccurrence subscribed
758+ touch $ switchSubscribedSelf subscribed
759+ touch $ switchSubscribedOwnInvalidator subscribed
760+ return result
761+ EventCoincidence c -> do
762+ subscribed <- getCoincidenceSubscribed c
763+ liftIO $ do
764+ result <- readIORef $ coincidenceSubscribedOccurrence subscribed
765+ touch $ coincidenceSubscribedOuter subscribed
766+ --TODO: do we need to touch the inner subscriber?
767+ return result
1324768
1325769-- Always refers to 0
1326770{-# NOINLINE zeroRef #-}
1327−zeroRef :: IORef Height
1328−zeroRef = unsafePerformIO $ newIORef zeroHeight
1329−
1330−type CanSubscribe x m =
1331− ( HasSpiderTimeline x
1332− , HasCurrentHeight x m
1333− , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
1334− , Defer SomeClear m -- Wiping out event firings
1335− , Defer (SomeAssignment x) m -- Updating state
1336− , Defer (SomeResetCoincidence x) m
1337− , m ~ EventM x
1338− )
1339−
1340−getRootSubscribed :: GCompare k => k a -> Root x k -> Subscriber x a -> IO (WeakBagTicket, RootSubscribed x a, Maybe a)
1341−getRootSubscribed k r sub = do
1342− mSubscribed <- readIORef $ rootSubscribed r
1343− let getOcc = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
771+zeroRef :: IORef Int
772+zeroRef = unsafePerformIO $ newIORef 0
773+
774+getEventSubscribed :: Event a -> EventM (EventSubscribed a)
775+getEventSubscribed e = case e of
776+ EventRoot k r -> liftM EventSubscribedRoot $ getRootSubscribed k r
777+ EventNever -> return EventSubscribedNever
778+ EventPush p -> liftM EventSubscribedPush $ getPushSubscribed p
779+ EventFan k f -> liftM (EventSubscribedFan k) $ getFanSubscribed f
780+ EventMerge m -> liftM EventSubscribedMerge $ getMergeSubscribed m
781+ EventSwitch s -> liftM EventSubscribedSwitch $ getSwitchSubscribed s
782+ EventCoincidence c -> liftM EventSubscribedCoincidence $ getCoincidenceSubscribed c
783+
784+debugSubscribe :: Bool
785+debugSubscribe = False
786+
787+subscribeEventSubscribed :: EventSubscribed a -> WeakSubscriber a -> IO ()
788+subscribeEventSubscribed es ws = case es of
789+ EventSubscribedRoot r -> do
790+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Root"
791+ modifyIORef' (rootSubscribedSubscribers r) (ws:)
792+ EventSubscribedNever -> do
793+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Never"
794+ return ()
795+ EventSubscribedPush subscribed -> do
796+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Push"
797+ modifyIORef' (pushSubscribedSubscribers subscribed) (ws:)
798+ EventSubscribedFan k subscribed -> do
799+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Fan"
800+ modifyIORef' (fanSubscribedSubscribers subscribed) $ DMap.insertWith (liftA2 (++)) (FanSubscriberKey k) (Identity [ws])
801+ EventSubscribedMerge subscribed -> do
802+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Merge"
803+ modifyIORef' (mergeSubscribedSubscribers subscribed) (ws:)
804+ EventSubscribedSwitch subscribed -> do
805+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Switch"
806+ modifyIORef' (switchSubscribedSubscribers subscribed) (ws:)
807+ EventSubscribedCoincidence subscribed -> do
808+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Coincidence"
809+ modifyIORef' (coincidenceSubscribedSubscribers subscribed) (ws:)
810+
811+getEventSubscribedOcc :: EventSubscribed a -> IO (Maybe a)
812+getEventSubscribedOcc es = case es of
813+ EventSubscribedRoot r -> rootSubscribedOccurrence r
814+ EventSubscribedNever -> return Nothing
815+ EventSubscribedPush subscribed -> readIORef $ pushSubscribedOccurrence subscribed
816+ EventSubscribedFan k subscribed -> do
817+ parentOcc <- getEventSubscribedOcc $ fanSubscribedParent subscribed
818+ let occ = coerce $ DMap.lookup k =<< parentOcc
819+ return occ
820+ EventSubscribedMerge subscribed -> readIORef $ mergeSubscribedOccurrence subscribed
821+ EventSubscribedSwitch subscribed -> readIORef $ switchSubscribedOccurrence subscribed
822+ EventSubscribedCoincidence subscribed -> readIORef $ coincidenceSubscribedOccurrence subscribed
823+
824+eventSubscribedHeightRef :: EventSubscribed a -> IORef Int
825+eventSubscribedHeightRef es = case es of
826+ EventSubscribedRoot _ -> zeroRef
827+ EventSubscribedNever -> zeroRef
828+ EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
829+ EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ fanSubscribedParent subscribed
830+ EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
831+ EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
832+ EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
833+
834+subscribe :: Event a -> WeakSubscriber a -> EventM (EventSubscribed a)
835+subscribe e ws = do
836+ subd <- getEventSubscribed e
837+ liftIO $ subscribeEventSubscribed subd ws
838+ return subd
839+
840+noinlineFalse :: Bool
841+noinlineFalse = False
842+{-# NOINLINE noinlineFalse #-}
843+
844+getRootSubscribed :: GCompare k => k a -> Root k -> EventM (RootSubscribed a)
845+getRootSubscribed k r = do
846+ mSubscribed <- liftIO $ readIORef $ rootSubscribed r
1344847 case DMap.lookup k mSubscribed of
1345− Just subscribed -> {-# SCC "hitRoot" #-} do
1346− sln <- subscribeRootSubscribed subscribed sub
1347− occ <- getOcc
1348− return (sln, subscribed, occ)
1349− Nothing -> {-# SCC "missRoot" #-} do
1350− weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1351− let !cached = rootSubscribed r
1352− uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
1353− (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
1354− when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1355− uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1356− writeIORef uninitRef $! uninit
1357− let !subscribed = RootSubscribed
1358− { rootSubscribedKey = k
1359− , rootSubscribedCachedSubscribed = cached
1360− , rootSubscribedOccurrence = getOcc
1361− , rootSubscribedSubscribers = subs
1362− , rootSubscribedUninit = uninit
1363− , rootSubscribedWeakSelf = weakSelf
848+ Just subscribed -> return subscribed
849+ Nothing -> liftIO $ do
850+ subscribersRef <- newIORef []
851+ subscribed <- newRootSubscribed (liftM (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r) subscribersRef
852+ -- Strangely, init needs the same stuff as a RootSubscribed has, but it must not be the same as the one that everyone's subscribing to, or it'll leak memory
853+ uninit <- rootInit r k $ RootTrigger (subscribersRef, rootOccurrence r, k)
854+ addFinalizer subscribed $ do
855+ when noinlineFalse $ putStr "" -- For some reason, without this line, the finalizer will run earlier than it should
856+-- putStrLn "Uninit root"
857+ uninit
858+ liftIO $ modifyIORef' (rootSubscribed r) $ DMap.insert k subscribed
859+ return subscribed
860+
861+-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
862+getPushSubscribed :: Push a b -> EventM (PushSubscribed a b)
863+getPushSubscribed p = do
864+ mSubscribed <- liftIO $ readIORef $ pushSubscribed p
865+ case mSubscribed of
866+ Just subscribed -> return subscribed
867+ Nothing -> do -- Not yet subscribed
868+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ pushSubscribed p
869+ s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
870+ ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberPush"
871+ subd <- subscribe (pushParent p) $ WeakSubscriberSimple ws
872+ parentOcc <- liftIO $ getEventSubscribedOcc subd
873+ occ <- liftM join $ mapM (pushCompute p) parentOcc
874+ occRef <- liftIO $ newIORef occ
875+ when (isJust occ) $ scheduleClear occRef
876+ subscribersRef <- liftIO $ newIORef []
877+ let subscribed = PushSubscribed
878+ { pushSubscribedOccurrence = occRef
879+ , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
880+ , pushSubscribedSubscribers = subscribersRef
881+ , pushSubscribedSelf = unsafeCoerce s
882+ , pushSubscribedParent = subd
1364883#ifdef DEBUG_NODEIDS
1365− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
884+ , pushSubscribedNodeId = unsafeNodeId p
1366885#endif
1367886 }
1368− -- If we die at the same moment that all our children die, they will
1369− -- try to clean us up but will fail because their Weak reference to us
1370− -- will also be dead. So, if we are dying, check if there are any
1371− -- children; since children don't bother cleaning themselves up if
1372− -- their parents are already dead, I don't think there's a race
1373− -- condition here. However, if there are any children, then we can
1374− -- infer that we need to clean ourselves up, so we do.
1375− finalCleanup = do
1376− cs <- readIORef $ _weakBag_children subs
1377− when (not $ IntMap.null cs) (cleanupRootSubscribed subscribed)
1378− writeIORef weakSelf =<< evaluate =<< mkWeakPtr subscribed (Just finalCleanup)
1379− 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
1380− occ <- getOcc
1381− return (sln, subscribed, occ)
1382−
1383−#ifdef USE_TEMPLATE_HASKELL
1384−{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
887+ liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
888+ return subscribed
889+
890+getMergeSubscribed :: forall k. GCompare k => Merge k -> EventM (MergeSubscribed k)
891+getMergeSubscribed m = {-# SCC "getMergeSubscribed.entire" #-} do
892+ mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
893+ case mSubscribed of
894+ Just subscribed -> return subscribed
895+ Nothing -> if DMap.null $ mergeParents m then emptyMergeSubscribed else do
896+ subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
897+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
898+ s <- liftIO $ newBox subscribedUnsafe
899+ ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberMerge"
900+ subscribers :: [(Any, Maybe (DSum k Identity), Int, DSum k EventSubscribed)] <- forM (DMap.toList $ mergeParents m) $ {-# SCC "getMergeSubscribed.a" #-} \(k :=> e) -> {-# SCC "getMergeSubscribed.a1" #-} do
901+ parentSubd <- {-# SCC "getMergeSubscribed.a.parentSubd" #-} subscribe e $ WeakSubscriberMerge k ws
902+ parentOcc <- {-# SCC "getMergeSubscribed.a.parentOcc" #-} liftIO $ getEventSubscribedOcc parentSubd
903+ height <- {-# SCC "getMergeSubscribed.a.height" #-} liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
904+ return $ {-# SCC "getMergeSubscribed.a.returnVal" #-} (unsafeCoerce s :: Any, fmap (\x -> k :=> Identity x) parentOcc, height, k :=> parentSubd)
905+ let dm = DMap.fromDistinctAscList $ catMaybes $ map (\(_, x, _, _) -> x) subscribers
906+ subscriberHeights = map (\(_, _, x, _) -> x) subscribers
907+ myHeight =
908+ if any (==invalidHeight) subscriberHeights --TODO: Replace 'any' with invalidHeight-preserving 'maximum'
909+ then invalidHeight
910+ else succ $ Prelude.maximum subscriberHeights -- This is safe because the DMap will never be empty here
911+ currentHeight <- getCurrentHeight
912+ let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
913+ then (if DMap.null dm then Nothing else Just dm, DMap.empty)
914+ else (Nothing, dm)
915+ when (not $ DMap.null accum) $ do
916+ scheduleMerge myHeight subscribedUnsafe
917+ occRef <- liftIO $ newIORef occ
918+ when (isJust occ) $ scheduleClear occRef
919+ accumRef <- liftIO $ newIORef accum
920+ heightRef <- liftIO $ newIORef myHeight
921+ subsRef <- liftIO $ newIORef []
922+ let subscribed = MergeSubscribed
923+ { mergeSubscribedOccurrence = occRef
924+ , mergeSubscribedAccum = accumRef
925+ , mergeSubscribedHeight = heightRef
926+ , mergeSubscribedSubscribers = subsRef
927+ , mergeSubscribedSelf = unsafeCoerce $ map (\(x, _, _, _) -> x) subscribers --TODO: Does lack of strictness make this leak?
928+ , mergeSubscribedParents = DMap.fromDistinctAscList $ map (\(_, _, _, x) -> x) subscribers
929+#ifdef DEBUG_NODEIDS
930+ , mergeSubscribedNodeId = unsafeNodeId m
1385931#endif
1386−cleanupRootSubscribed :: RootSubscribed x a -> IO ()
1387−cleanupRootSubscribed self@RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached } = do
1388− rootSubscribedUninit self
1389− modifyIORef' cached $ DMap.delete k
1390−
1391−{-# INLINE subscribeRootSubscribed #-}
1392−subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1393−subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1394−
1395−{-# INLINABLE getFanSubscribed #-}
1396−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)
1397−getFanSubscribed k f sub = do
932+ }
933+ liftIO $ writeIORef subscribedRef subscribed
934+ return subscribed
935+ where emptyMergeSubscribed = do --TODO: This should never happen
936+ occRef <- liftIO $ newIORef Nothing
937+ accumRef <- liftIO $ newIORef DMap.empty
938+ subsRef <- liftIO $ newIORef []
939+ return $ MergeSubscribed
940+ { mergeSubscribedOccurrence = occRef
941+ , mergeSubscribedAccum = accumRef
942+ , mergeSubscribedHeight = zeroRef
943+ , mergeSubscribedSubscribers = subsRef --TODO: This will definitely leak
944+ , mergeSubscribedSelf = unsafeCoerce ()
945+ , mergeSubscribedParents = DMap.empty
946+#ifdef DEBUG_NODEIDS
947+ , mergeSubscribedNodeId = -1
948+#endif
949+ }
950+
951+getFanSubscribed :: GCompare k => Fan k -> EventM (FanSubscribed k)
952+getFanSubscribed f = do
1398953 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1399954 case mSubscribed of
1400− Just subscribed -> {-# SCC "hitFan" #-} liftIO $ do
1401− sln <- subscribeFanSubscribed k subscribed sub
1402− occ <- readIORef $ fanSubscribedOccurrence subscribed
1403− return (sln, subscribed, coerce $ DMap.lookup k =<< occ)
1404− Nothing -> {-# SCC "missFan" #-} do
1405− subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1406− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1407− s <- liftIO $ newSubscriberFan subscribedUnsafe
1408− (subscription, parentOcc) <- subscribeAndRead (fanParent f) s
1409− weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1410− (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1411− subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1412− occRef <- liftIO $ newIORef parentOcc
1413− when (isJust parentOcc) $ scheduleClear occRef
955+ Just subscribed -> return subscribed
956+ Nothing -> do
957+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ fanSubscribed f
958+ sub <- liftIO $ newSubscriberFan subscribedUnsafe
959+ wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberFan"
960+ subd <- subscribe (fanParent f) $ WeakSubscriberSimple wsub
961+ subscribersRef <- liftIO $ newIORef DMap.empty
1414962 let subscribed = FanSubscribed
1415− { fanSubscribedCachedSubscribed = fanSubscribed f
1416− , fanSubscribedOccurrence = occRef
1417− , fanSubscribedParent = subscription
963+ { fanSubscribedParent = subd
1418964 , fanSubscribedSubscribers = subscribersRef
965+ , fanSubscribedSelf = sub
1419966#ifdef DEBUG_NODEIDS
1420967 , fanSubscribedNodeId = unsafeNodeId f
1421968#endif
1422969 }
1423− let !self = (k, subscribed)
1424− liftIO $ writeIORef subscribersRef $! DMap.singleton k $ FanSubscribedChildren subsForK self weakSelf
1425− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1426− liftIO $ writeIORef subscribedRef $! subscribed
1427970 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1428− return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1429−
1430−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1431−cleanupFanSubscribed (k, subscribed) = do
1432− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1433− let reducedSubscribers = DMap.delete k subscribers
1434− if DMap.null reducedSubscribers
1435− then do
1436− unsubscribe $ fanSubscribedParent subscribed
1437− -- Not necessary in this case, because this whole FanSubscribed is dead: writeIORef (fanSubscribedSubscribers subscribed) reducedSubscribers
1438− writeIORef (fanSubscribedCachedSubscribed subscribed) Nothing
1439− else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1440−
1441−{-# INLINE subscribeFanSubscribed #-}
1442−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1443−subscribeFanSubscribed k subscribed sub = do
1444− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1445− case DMap.lookup k subscribers of
1446− Nothing -> {-# SCC "missSubscribeFanSubscribed" #-} do
1447− let !self = (k, subscribed)
1448− weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1449− (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1450− 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
1451− return sln
1452− Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1453−
1454−{-# INLINABLE getSwitchSubscribed #-}
1455−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)
1456−getSwitchSubscribed s sub = do
971+ return subscribed
972+
973+getSwitchSubscribed :: Switch a -> EventM (SwitchSubscribed a)
974+getSwitchSubscribed s = do
1457975 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1458976 case mSubscribed of
1459− Just subscribed -> {-# SCC "hitSwitch" #-} liftIO $ do
1460− sln <- subscribeSwitchSubscribed subscribed sub
1461− occ <- readIORef $ switchSubscribedOccurrence subscribed
1462− return (sln, subscribed, occ)
1463− Nothing -> {-# SCC "missSwitch" #-} do
977+ Just subscribed -> return subscribed
978+ Nothing -> do
1464979 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1465980 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1466981 i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1467− mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
982+ sub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1468983 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1469984 wiRef <- liftIO $ newIORef wi
985+ wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberSwitch"
986+ selfWeakRef <- liftIO $ newIORef wsub
1470987 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1471− holdInits <- getDeferralQueue
1472− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1473− (subscription@(EventSubscription _ subd), parentOcc) <- subscribeAndRead e mySub
1474− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
1475− subscriptionRef <- liftIO $ newIORef subscription
988+ e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) $ Just (wi, parentsRef)
989+ subd <- subscribe e $ WeakSubscriberSimple wsub
990+ subdRef <- liftIO $ newIORef subd
991+ parentOcc <- liftIO $ getEventSubscribedOcc subd
1476992 occRef <- liftIO $ newIORef parentOcc
1477993 when (isJust parentOcc) $ scheduleClear occRef
1478− weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1479− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1480− let !subscribed = SwitchSubscribed
1481− { switchSubscribedCachedSubscribed = switchSubscribed s
1482− , switchSubscribedOccurrence = occRef
994+ heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
995+ subscribersRef <- liftIO $ newIORef []
996+ let subscribed = SwitchSubscribed
997+ { switchSubscribedOccurrence = occRef
1483998 , switchSubscribedHeight = heightRef
1484− , switchSubscribedSubscribers = subs
999+ , switchSubscribedSubscribers = subscribersRef
1000+ , switchSubscribedSelf = sub
1001+ , switchSubscribedSelfWeak = selfWeakRef
14851002 , switchSubscribedOwnInvalidator = i
14861003 , switchSubscribedOwnWeakInvalidator = wiRef
14871004 , switchSubscribedBehaviorParents = parentsRef
14881005 , switchSubscribedParent = switchParent s
1489− , switchSubscribedCurrentParent = subscriptionRef
1490− , switchSubscribedWeakSelf = weakSelf
1006+ , switchSubscribedCurrentParent = subdRef
14911007#ifdef DEBUG_NODEIDS
14921008 , switchSubscribedNodeId = unsafeNodeId s
14931009#endif
14941010 }
1495− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1496− liftIO $ writeIORef subscribedRef $! subscribed
1011+ liftIO $ writeIORef subscribedRef subscribed
14971012 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1498− return (slnForSub, subscribed, parentOcc)
1499−
1500−cleanupSwitchSubscribed :: SwitchSubscribed x a -> IO ()
1501−cleanupSwitchSubscribed subscribed = do
1502− unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1503− finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1504− writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1013+ return subscribed
15051014
1506−{-# INLINE subscribeSwitchSubscribed #-}
1507−subscribeSwitchSubscribed :: SwitchSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1508−subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1509−
1510−{-# INLINABLE getCoincidenceSubscribed #-}
1511−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)
1512−getCoincidenceSubscribed c sub = do
1015+getCoincidenceSubscribed :: forall a. Coincidence a -> EventM (CoincidenceSubscribed a)
1016+getCoincidenceSubscribed c = do
15131017 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
15141018 case mSubscribed of
1515− Just subscribed -> {-# SCC "hitCoincidence" #-} liftIO $ do
1516− sln <- subscribeCoincidenceSubscribed subscribed sub
1517− occ <- readIORef $ coincidenceSubscribedOccurrence subscribed
1518− return (sln, subscribed, occ)
1519− Nothing -> {-# SCC "missCoincidence" #-} do
1019+ Just subscribed -> return subscribed
1020+ Nothing -> do
15201021 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
15211022 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
15221023 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1523− (outerSubscription@(EventSubscription _ outerSubd), outerOcc) <- subscribeAndRead (coincidenceParent c) subOuter
1524− outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1024+ wsubOuter <- liftIO $ mkWeakPtrWithDebug subOuter "subOuter"
1025+ outerSubd <- subscribe (coincidenceParent c) $ WeakSubscriberSimple wsubOuter
1026+ outerOcc <- liftIO $ getEventSubscribedOcc outerSubd
1027+ outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
15251028 (occ, height, mInnerSubd) <- case outerOcc of
15261029 Nothing -> return (Nothing, outerHeight, Nothing)
15271030 Just o -> do
@@ -1532,452 +1035,270 @@ getCoincidenceSubscribed c sub = do
15321035 heightRef <- liftIO $ newIORef height
15331036 innerSubdRef <- liftIO $ newIORef mInnerSubd
15341037 scheduleClear innerSubdRef
1535− weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1536− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1038+ subscribersRef <- liftIO $ newIORef []
15371039 let subscribed = CoincidenceSubscribed
1538− { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1539− , coincidenceSubscribedOccurrence = occRef
1040+ { coincidenceSubscribedOccurrence = occRef
15401041 , coincidenceSubscribedHeight = heightRef
1541− , coincidenceSubscribedSubscribers = subs
1042+ , coincidenceSubscribedSubscribers = subscribersRef
15421043 , coincidenceSubscribedOuter = subOuter
1543− , coincidenceSubscribedOuterParent = outerSubscription
1044+ , coincidenceSubscribedOuterParent = outerSubd
15441045 , coincidenceSubscribedInnerParent = innerSubdRef
1545− , coincidenceSubscribedWeakSelf = weakSelf
15461046#ifdef DEBUG_NODEIDS
15471047 , coincidenceSubscribedNodeId = unsafeNodeId c
15481048#endif
15491049 }
1550− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1551− liftIO $ writeIORef subscribedRef $! subscribed
1050+ liftIO $ writeIORef subscribedRef subscribed
15521051 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1553− return (slnForSub, subscribed, occ)
1554−
1555−cleanupCoincidenceSubscribed :: CoincidenceSubscribed x a -> IO ()
1556−cleanupCoincidenceSubscribed subscribed = do
1557− unsubscribe $ coincidenceSubscribedOuterParent subscribed
1558− writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1559−
1560−{-# INLINE subscribeCoincidenceSubscribed #-}
1561−subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1562−subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1563−
1564−{-# INLINE merge #-}
1565−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1566−merge d = cacheEvent (mergeCheap d)
1567−
1568−{-# INLINE mergeWithMove #-}
1569−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1570−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1571−
1572−{-# INLINE [1] mergeCheap #-}
1573−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1574−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1575− where
1576− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1577− updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1578− let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1579− (mOldSubd, newPs) <- case me of
1580− Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1581− Just e -> do
1582− let s = subscriber $ return k
1583− subscription@(EventSubscription _ subd) <- subscribe e s
1584− newParentHeight <- liftIO $ getEventSubscribedHeight subd
1585− let newParent = MergeSubscribedParent subscription
1586− liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1587− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1588− forM_ mOldSubd $ \oldSubd -> do
1589− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ unMergeSubscribedParent oldSubd
1590− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1591− return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1592− foldM f ([], oldParents) $ DMap.toList p
1593− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1594− getInitialSubscribers initialParents subscriber = do
1595− subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1596− let s = subscriber $ return k
1597− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1598− height <- liftIO $ getEventSubscribedHeight parentSubd
1599− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1600− return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1601− , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1602− , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1603− )
1604− destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1605− destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1606−
1607−{-# INLINE [1] mergeCheapWithMove #-}
1608−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1609−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1610− where
1611− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1612− updateMe subscriber heightBagRef oldParents pWrapped = do
1613− let p = unPatchDMapWithMove pWrapped
1614− let f (subscriptionsToKill, ps) (k :=> edit) = do
1615− let deleteHere = return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1616− insertHere e = do
1617− keyRef <- liftIO $ newIORef k
1618− let s = subscriber $ liftIO $ readIORef keyRef
1619− subscription@(EventSubscription _ subd) <- subscribe e s
1620− liftIO $ do
1621− newParentHeight <- getEventSubscribedHeight subd
1622− let newParent = MergeSubscribedParentWithMove subscription keyRef
1623− modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1624− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1625− (mOldSubd, newPs) <- case edit of
1626− DMapEdit_Delete moved -> do
1627− (toDelete, newPs) <- deleteHere
1628− return (if moved then Nothing else toDelete, newPs)
1629− DMapEdit_Insert moved e -> do
1630− (toDelete, newPs) <- insertHere e
1631− return (if moved then Nothing else toDelete, newPs)
1632− DMapEdit_Move moved sourceKey -> do
1633− (toDelete, newPs) <- case DMap.lookup sourceKey ps of
1634− Nothing -> deleteHere
1635− Just s@(MergeSubscribedParentWithMove _ keyRef) -> do
1636− liftIO $ writeIORef keyRef k
1637− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k s ps
1638− return (if moved then Nothing else toDelete, newPs)
1639− forM_ mOldSubd $ \oldSubd -> do
1640− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription oldSubd
1641− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1642− return (maybeToList (_mergeSubscribedParentWithMove_subscription <$> mOldSubd) ++ subscriptionsToKill, newPs)
1643− foldM f ([], oldParents) $ DMap.toList p
1644− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1645− getInitialSubscribers initialParents subscriber = do
1646− subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1647− keyRef <- liftIO $ newIORef k
1648− let s = subscriber $ liftIO $ readIORef keyRef
1649− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1650− height <- liftIO $ getEventSubscribedHeight parentSubd
1651− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1652− return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1653− , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1654− , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1655− )
1656− destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1657− destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1658−
1659−type MergeUpdateFunc k x p s
1660− = (forall a. EventM x (k a) -> Subscriber x a)
1661− -> IORef HeightBag
1662− -> DMap k s
1663− -> p
1664− -> EventM x ([EventSubscription x], DMap k s)
1665−
1666−type MergeInitFunc k x s
1667− = DMap k (Event x)
1668− -> (forall a. EventM x (k a) -> Subscriber x a)
1669− -> EventM x (DMap k Identity, [Height], DMap k s)
1670−
1671−type MergeDestroyFunc k s
1672− = DMap k s
1673− -> IO ()
1674−
1675−{-# INLINE mergeCheap' #-}
1676−mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> (DMap k s -> Int) -> Dynamic x p -> Event x (DMap k Identity)
1677−mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub -> do
1678− initialParents <- readBehaviorUntracked $ dynamicCurrent d
1679− accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1680− heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1681− heightBagRef <- liftIO $ newIORef $ error "merge: heightBagRef not yet initialized"
1682− parentsRef :: IORef (DMap k s) <- liftIO $ newIORef $ error "merge: parentsRef not yet initialized"
1683− let scheduleSelf height = scheduleMerge' height heightRef $ do
1684− m <- liftIO $ readIORef accumRef
1685− liftIO $ writeIORef accumRef $! DMap.empty
1686− --TODO: Assert that m is not empty
1687− subscriberPropagate sub m
1688− invalidateMyHeight = do
1689− oldHeight <- readIORef heightRef
1690− 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
1691− writeIORef heightRef $! invalidHeight
1692− subscriberInvalidateHeight sub oldHeight
1693− revalidateMyHeight = do
1694− currentHeight <- readIORef heightRef
1695− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1696− heights <- readIORef heightBagRef
1697− parents <- readIORef parentsRef
1698− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1699− case heightBagSize heights `compare` numParents parents of
1700− LT -> return ()
1701− EQ -> do
1702− let height = succHeight $ heightBagMax heights
1703− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1704− writeIORef heightRef $! height
1705− subscriberRecalculateHeight sub height
1706− GT -> error $ "revalidateMergeHeight: more heights than parents for Merge"
1707− subscriber :: forall a. EventM x (k a) -> Subscriber x a
1708− subscriber getKey = Subscriber
1709− { subscriberPropagate = \a -> do
1710− oldM <- liftIO $ readIORef accumRef
1711− k <- getKey
1712− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1713− tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1714− liftIO $ writeIORef accumRef $! newM
1715− when (DMap.null oldM) $ do -- Only schedule the firing once
1716− height <- liftIO $ readIORef heightRef
1717− --TODO: assertions about height
1718− currentHeight <- getCurrentHeight
1719− when (height <= currentHeight) $ do
1720− if height /= invalidHeight
1721− then do
1722− myStack <- liftIO $ whoCreatedIORef undefined --TODO
1723− error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1724− else liftIO $ do
1725−#ifdef DEBUG_CYCLES
1726− nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1727− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1728− let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1729−#else
1730− let cycleInfo = ""
1731−#endif
1732− error $ "Causality loop found" <> cycleInfo
1733− scheduleSelf height
1734− , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1735− modifyIORef' heightBagRef $ heightBagRemove old
1736− invalidateMyHeight
1737− , subscriberRecalculateHeight = \new -> do
1738− modifyIORef' heightBagRef $ heightBagAdd new
1739− revalidateMyHeight
1740− }
1741− let updateMe x = do
1742− oldParents <- liftIO $ readIORef parentsRef
1743− (subscriptionsToKill, newParents) <- updateFunc subscriber heightBagRef oldParents x
1744− liftIO $ writeIORef parentsRef $! newParents
1745− return subscriptionsToKill
1746− (dm, heights, initialParentState) <- getInitialSubscribers initialParents subscriber
1747− let myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1748− myHeight = if invalidHeight `elem` heights
1749− then invalidHeight
1750− else succHeight $ heightBagMax myHeightBag
1751− currentHeight <- getCurrentHeight
1752− let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1753− then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1754− else (Nothing, dm)
1755− unless (DMap.null accum) $ do
1756− scheduleSelf myHeight
1757− occRef <- liftIO $ newIORef occ
1758− when (isJust occ) $ scheduleClear occRef
1759− liftIO $ writeIORef accumRef $! accum
1760− liftIO $ writeIORef heightRef $! myHeight
1761− liftIO $ writeIORef heightBagRef $! myHeightBag
1762− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1763− liftIO $ writeIORef parentsRef $! initialParentState
1764− defer $ SomeMergeInit $ do
1765− let s = Subscriber
1766− { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
1767− tracePropagate (Proxy :: Proxy x) $ "SubscriberMerge/Change"
1768− defer $ SomeMergeUpdate (updateMe a) invalidateMyHeight revalidateMyHeight
1769− , subscriberInvalidateHeight = \_ -> return ()
1770− , subscriberRecalculateHeight = \_ -> return ()
1771− }
1772− (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
1773− forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1774− -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
1775− -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
1776− -- its subscription.
1777− liftIO $ writeIORef changeSubdRef (s, changeSubscription)
1778− let unsubscribeAll = destroy =<< readIORef parentsRef
1779− return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parentsRef, changeSubdRef)
1780− , occ
1781− )
1782−
1783−newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
1784−
1785−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
1052+ return subscribed
1053+
1054+merge :: GCompare k => DMap k Event -> Event (DMap k Identity)
1055+merge m = EventMerge $ Merge
1056+ { mergeParents = m
1057+ , mergeSubscribed = unsafeNewIORef m Nothing
1058+ }
1059+
1060+newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
1061+
1062+fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
17861063fan e =
17871064 let f = Fan
17881065 { fanParent = e
17891066 , fanSubscribed = unsafeNewIORef e Nothing
17901067 }
1791− in EventSelector $ \k -> eventFan k f
1792−
1793−runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeMergeInit x] -> EventM x ()
1794−runHoldInits holdInitRef mergeInitRef = do
1795− holdInits <- liftIO $ readIORef holdInitRef
1796− mergeInits <- liftIO $ readIORef mergeInitRef
1797− unless (null holdInits && null mergeInits) $ do
1798− liftIO $ writeIORef holdInitRef []
1799− liftIO $ writeIORef mergeInitRef []
1800− mapM_ initHold holdInits
1801− mapM_ unSomeMergeInit mergeInits
1802− runHoldInits holdInitRef mergeInitRef
1803−
1804−initHold :: HasSpiderTimeline x => SomeHoldInit x -> EventM x ()
1805−initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1806−
1807−newEventEnv :: IO (EventEnv x)
1808−newEventEnv = do
1068+ in EventSelector $ \k -> EventFan k f
1069+
1070+-- | Run an event action outside of a frame
1071+runFrame :: EventM a -> IO a
1072+runFrame a = do
18091073 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
18101074 holdInitRef <- newIORef []
1811− mergeUpdateRef <- newIORef []
1812− mergeInitRef <- newIORef []
1813− heightRef <- newIORef zeroHeight
1075+ heightRef <- newIORef 0
18141076 toClearRef <- newIORef []
18151077 toClearRootRef <- newIORef []
18161078 coincidenceInfosRef <- newIORef []
1817− delayedRef <- newIORef IntMap.empty
1818− return $ EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1819−
1820−clearEventEnv :: EventEnv x -> IO ()
1821−clearEventEnv (EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
1822− writeIORef toAssignRef []
1823− writeIORef holdInitRef []
1824− writeIORef mergeUpdateRef []
1825− writeIORef mergeInitRef []
1826− writeIORef heightRef zeroHeight
1827− writeIORef toClearRef []
1828− writeIORef toClearRootRef []
1829− writeIORef coincidenceInfosRef []
1830− writeIORef delayedRef IntMap.empty
1831−
1832−-- | Run an event action outside of a frame
1833−runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1834−runFrame a = SpiderHost $ ask >>= \_ -> lift $ do
1835− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
1836− let go = do
1837− result <- a
1838− 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
1839− return result
1840− result <- runEventM go
1841− toClear <- readIORef $ eventEnvClears env
1842− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1843− toClearRoot <- readIORef $ eventEnvRootClears env
1844− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1845− toAssign <- readIORef $ eventEnvAssignments env
1079+ delayedRef <- liftIO $ newIORef IntMap.empty
1080+ result <- flip runEventM (EventEnv toAssignRef holdInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) $ do
1081+ result <- a
1082+ let runHoldInits = do
1083+ holdInits <- liftIO $ readIORef holdInitRef
1084+ if null holdInits then return () else do
1085+ liftIO $ writeIORef holdInitRef []
1086+ forM_ holdInits $ \(SomeHoldInit e h) -> subscribeHold e h
1087+ runHoldInits
1088+ runHoldInits -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
1089+ return result
1090+ toClear <- readIORef toClearRef
1091+ forM_ toClear $ \(SomeMaybeIORef ref) -> writeIORef ref Nothing
1092+ toClearRoot <- readIORef toClearRootRef
1093+ forM_ toClearRoot $ \(SomeDMapIORef ref) -> writeIORef ref DMap.empty
1094+ toAssign <- readIORef toAssignRef
18461095 toReconnectRef <- newIORef []
1847− coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
1848− forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1849− writeIORef vRef v
1850− when debugInvalidate $ putStrLn $ "Invalidating Hold"
1851− writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1852− mergeUpdates <- readIORef $ eventEnvMergeUpdates env
1853− writeIORef (eventEnvMergeUpdates env) []
1854− when debugPropagate $ putStrLn "Updating merges"
1855− mergeSubscriptionsToKill <- runEventM $ concat <$> mapM _someMergeUpdate_update mergeUpdates
1856− when debugPropagate $ putStrLn "Updating merges done"
1096+ forM_ toAssign $ \(SomeAssignment h v) -> do
1097+ writeIORef (holdValue h) v
1098+ writeIORef (holdInvalidators h) =<< invalidate toReconnectRef =<< readIORef (holdInvalidators h)
1099+ coincidenceInfos <- readIORef coincidenceInfosRef
1100+ forM_ coincidenceInfos $ \(SomeCoincidenceInfo wsubInner subInner mcs) -> do
1101+ touch subInner
1102+ finalize wsubInner
1103+ mapM_ invalidateCoincidenceHeight mcs
18571104 toReconnect <- readIORef toReconnectRef
1858− clearEventEnv env
1859− switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1860− oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1105+ forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1106+ wsub <- readIORef $ switchSubscribedSelfWeak subscribed
1107+ finalize wsub
18611108 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1862− when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
18631109 finalize wi
1864− i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1110+ let !i = switchSubscribedOwnInvalidator subscribed
18651111 wi' <- mkWeakPtrWithDebug i "wi'"
1866− writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
18671112 writeIORef (switchSubscribedBehaviorParents subscribed) []
1868− writeIORef (eventEnvHoldInits env) [] --TODO: Should we reuse this?
1869− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) $ eventEnvHoldInits env
1870− 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
1113+ e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed))
18711114 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1872− sub <- newSubscriberSwitch subscribed
1873− 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
1115+ let !sub = switchSubscribedSelf subscribed -- Must be done strictly, or the weak pointer will refer to a useless thunk
1116+ wsub' <- mkWeakPtrWithDebug sub "wsub'"
1117+ writeIORef (switchSubscribedSelfWeak subscribed) wsub'
1118+ subd' <- runFrame $ subscribe e $ WeakSubscriberSimple wsub' --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
18741119 {-
1875− stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1120+ stackTrace <- liftIO $ liftM renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
18761121 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
18771122 EventRoot _ -> "EventRoot"
18781123 EventNever -> "EventNever"
18791124 _ -> "something else"
18801125 -}
1881− writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1882− return oldSubscription
1883− liftIO $ mapM_ unsubscribe mergeSubscriptionsToKill
1884− liftIO $ mapM_ unsubscribe switchSubscriptionsToKill
1885− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1886− EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1887− parentHeight <- getEventSubscribedHeight subd'
1126+ writeIORef (switchSubscribedCurrentParent subscribed) subd'
1127+ parentHeight <- readIORef $ eventSubscribedHeightRef subd'
18881128 myHeight <- readIORef $ switchSubscribedHeight subscribed
1889− when (parentHeight /= myHeight) $ do
1890− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1891− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1892− 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
1893− forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1894− unsubscribe subscription
1895− mapM_ invalidateCoincidenceHeight mcs
1896− forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1897− mapM_ _someMergeUpdate_recalculateHeight mergeUpdates
1129+ if parentHeight == myHeight then return () else do
1130+ writeIORef (switchSubscribedHeight subscribed) parentHeight
1131+ mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1132+ forM_ coincidenceInfos $ \(SomeCoincidenceInfo _ _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
18981133 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1899− height <- calculateSwitchHeight subscribed
1900− updateSwitchHeight height subscribed
1134+ mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
19011135 return result
19021136
1903−newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1904−
1905−{-# INLINE zeroHeight #-}
1906−zeroHeight :: Height
1907−zeroHeight = Height 0
1908−
1909−{-# INLINE invalidHeight #-}
1910−invalidHeight :: Height
1911−invalidHeight = Height (-1000)
1912−
1913−#ifdef DEBUG_CYCLES
1914−-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1915−{-# INLINE invalidHeightBeingTraversed #-}
1916−invalidHeightBeingTraversed :: Height
1917−invalidHeightBeingTraversed = Height (-1001)
1918−#endif
1919−
1920−{-# INLINE succHeight #-}
1921−succHeight :: Height -> Height
1922−succHeight h@(Height a) =
1923− if h == invalidHeight
1924− then invalidHeight
1925− else Height $ succ a
1926−
1927−invalidateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1137+invalidHeight :: Int
1138+invalidHeight = -1000
1139+
1140+invalidateSubscriberHeight :: WeakSubscriber a -> IO ()
1141+invalidateSubscriberHeight ws = do
1142+ ms <- deRefWeakSubscriber ws
1143+ case ms of
1144+ Nothing -> return () --TODO: cleanup?
1145+ Just s -> case s of
1146+ SubscriberPush _ subscribed -> do
1147+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1148+ mapM_ invalidateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1149+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1150+ SubscriberMerge _ subscribed -> do
1151+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1152+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1153+ when (oldHeight /= invalidHeight) $ do
1154+ writeIORef (mergeSubscribedHeight subscribed) $ invalidHeight
1155+ mapM_ invalidateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1156+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1157+ SubscriberFan subscribed -> do
1158+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1159+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1160+ forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ invalidateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
1161+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1162+ SubscriberHold _ -> return ()
1163+ SubscriberSwitch subscribed -> do
1164+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1165+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1166+ when (oldHeight /= invalidHeight) $ do
1167+ writeIORef (switchSubscribedHeight subscribed) $ invalidHeight
1168+ mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1169+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1170+ SubscriberCoincidenceOuter subscribed -> do
1171+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1172+ invalidateCoincidenceHeight subscribed
1173+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1174+ SubscriberCoincidenceInner subscribed -> do
1175+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1176+ invalidateCoincidenceHeight subscribed
1177+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1178+
1179+invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
19281180invalidateCoincidenceHeight subscribed = do
19291181 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
19301182 when (oldHeight /= invalidHeight) $ do
1931− writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1932− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1933−
1934−updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
1935−updateSwitchHeight new subscribed = do
1936− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1937− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1938− when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1939− writeIORef (switchSubscribedHeight subscribed) $! new
1940− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1941−
1942−recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1183+ writeIORef (coincidenceSubscribedHeight subscribed) $ invalidHeight
1184+ mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
1185+
1186+--TODO: The recalculation algorithm seems a bit funky; make sure it doesn't miss stuff or hit stuff twice; also, it should probably be lazy
1187+
1188+recalculateSubscriberHeight :: WeakSubscriber a -> IO ()
1189+recalculateSubscriberHeight ws = do
1190+ ms <- deRefWeakSubscriber ws
1191+ case ms of
1192+ Nothing -> return () --TODO: cleanup?
1193+ Just s -> case s of
1194+ SubscriberPush _ subscribed -> do
1195+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1196+ mapM_ recalculateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1197+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1198+ SubscriberMerge _ subscribed -> do
1199+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1200+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1201+ when (oldHeight == invalidHeight) $ do
1202+ height <- calculateMergeHeight subscribed
1203+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1204+ when (height /= invalidHeight) $ do -- If height == invalidHeight, that means some of the prereqs have not yet been recomputed; when they do recompute, they'll catch this node again --TODO: this is O(n*m), where n is the number of children of this noe and m is the number that have been invalidated
1205+ writeIORef (mergeSubscribedHeight subscribed) height
1206+ mapM_ recalculateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1207+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1208+ SubscriberFan subscribed -> do
1209+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1210+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1211+ forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ recalculateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
1212+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1213+ SubscriberHold _ -> return ()
1214+ SubscriberSwitch subscribed -> do
1215+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1216+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1217+ when (oldHeight == invalidHeight) $ do
1218+ height <- calculateSwitchHeight subscribed
1219+ when (height /= invalidHeight) $ do
1220+ writeIORef (switchSubscribedHeight subscribed) height
1221+ mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1222+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1223+ SubscriberCoincidenceOuter subscribed -> do
1224+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1225+ void $ recalculateCoincidenceHeight subscribed
1226+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1227+ SubscriberCoincidenceInner subscribed -> do
1228+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1229+ void $ recalculateCoincidenceHeight subscribed
1230+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1231+
1232+recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
19431233recalculateCoincidenceHeight subscribed = do
19441234 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1945− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1235+ when (oldHeight == invalidHeight) $ do
19461236 height <- calculateCoincidenceHeight subscribed
19471237 when (height /= invalidHeight) $ do
1948− writeIORef (coincidenceSubscribedHeight subscribed) $! height
1949− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1238+ writeIORef (coincidenceSubscribedHeight subscribed) height
1239+ mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed) --TODO: This should probably be mandatory, just like with the merge and switch ones
19501240
1951−calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
1952−calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1241+calculateMergeHeight :: MergeSubscribed k -> IO Int
1242+calculateMergeHeight subscribed = if DMap.null (mergeSubscribedParents subscribed) then return 0 else do
1243+ heights <- forM (DMap.toList $ mergeSubscribedParents subscribed) $ \(_ :=> es) -> do
1244+ readIORef $ eventSubscribedHeightRef es
1245+ return $ if any (== invalidHeight) heights then invalidHeight else succ $ Prelude.maximum heights --TODO: Replace 'any' with invalidHeight-preserving 'maximum'
19531246
1954−calculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO Height
1247+calculateSwitchHeight :: SwitchSubscribed a -> IO Int
1248+calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef =<< readIORef (switchSubscribedCurrentParent subscribed)
1249+
1250+calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Int
19551251calculateCoincidenceHeight subscribed = do
1956− outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1957− innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1252+ outerHeight <- readIORef $ eventSubscribedHeightRef $ coincidenceSubscribedOuterParent subscribed
1253+ innerHeight <- maybe (return 0) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
19581254 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
19591255
1960−data SomeSwitchSubscribed x = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed x a)
1256+{-
1257+recalculateEventSubscribedHeight :: EventSubscribed a -> IO Int
1258+recalculateEventSubscribedHeight es = case es of
1259+ EventSubscribedRoot _ -> return 0
1260+ EventSubscribedNever -> return 0
1261+ EventSubscribedPush subscribed -> recalculateEventSubscribedHeight $ pushSubscribedParent subscribed
1262+ EventSubscribedMerge subscribed -> do
1263+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1264+ if oldHeight /= invalidHeight then return oldHeight else do
1265+ height <- calculateMergeHeight subscribed
1266+ writeIORef (mergeSubscribedHeight subscribed) height
1267+ return height
1268+ EventSubscribedFan _ subscribed -> recalculateEventSubscribedHeight $ fanSubscribedParent subscribed
1269+ EventSubscribedSwitch subscribed -> do
1270+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1271+ if oldHeight /= invalidHeight then return oldHeight else do
1272+ height <- calculateSwitchHeight subscribed
1273+ writeIORef (switchSubscribedHeight subscribed) height
1274+ return height
1275+ EventSubscribedCoincidence subscribed -> recalculateCoincidenceHeight subscribed
1276+-}
1277+
1278+data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed (SwitchSubscribed a)
1279+
1280+debugInvalidate :: Bool
1281+debugInvalidate = False
19611282
1962−invalidate :: IORef [SomeSwitchSubscribed x] -> WeakList (Invalidator x) -> IO (WeakList (Invalidator x))
1283+invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
19631284invalidate toReconnectRef wis = do
19641285 forM_ wis $ \wi -> do
19651286 mi <- deRefWeak wi
19661287 case mi of
19671288 Nothing -> do
1968− traceInvalidate "invalidate Dead"
1289+ when debugInvalidate $ liftIO $ putStrLn "invalidate Dead"
19691290 return () --TODO: Should we clean this up here?
19701291 Just i -> do
19711292 finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
19721293 case i of
19731294 InvalidatorPull p -> do
1974− traceInvalidate $ "invalidate: Pull" <> showNodeId p
1295+ when debugInvalidate $ liftIO $ putStrLn "invalidate Pull"
19751296 mVal <- readIORef $ pullValue p
19761297 forM_ mVal $ \val -> do
19771298 writeIORef (pullValue p) Nothing
1978− writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1299+ writeIORef (pullSubscribedInvalidators val) =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
19791300 InvalidatorSwitch subscribed -> do
1980− traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
1301+ when debugInvalidate $ liftIO $ putStrLn "invalidate Switch"
19811302 modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
19821303 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
19831304
@@ -1985,172 +1306,90 @@ invalidate toReconnectRef wis = do
19851306-- Reflex integration
19861307--------------------------------------------------------------------------------
19871308
1988−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1989−-- When we're specializing, we include this from Reflex.Class instead, because
1990−-- it needs access to these types; when we're not, we want them here, so our
1991−-- instance isn't an orphan instance.
1992−#include "SpiderTimeline.include.hs"
1993−#include "SpiderTimelineHost.include.hs"
1994−#endif
1995−
1996−unsafeNewSpiderTimelineEnv :: forall x. IO (SpiderTimelineEnv x)
1997−unsafeNewSpiderTimelineEnv = do
1998− lock <- newMVar ()
1999− env <- newEventEnv
2000−#ifdef DEBUG
2001− depthRef <- newIORef 0
2002−#endif
2003− return $ SpiderTimelineEnv
2004− { _spiderTimeline_lock = lock
2005− , _spiderTimeline_eventEnv = env
2006−#ifdef DEBUG
2007− , _spiderTimeline_depth = depthRef
2008−#endif
2009− }
2010−
2011−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
2012−-- | Create a new SpiderTimelineEnv
2013−newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2014−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2015−
2016−data LocalSpiderTimeline x s
2017−
2018−instance Reifies s (SpiderTimelineEnv x) =>
2019− HasSpiderTimeline (LocalSpiderTimeline x s) where
2020− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2021−
2022−localSpiderTimeline
2023− :: Proxy s
2024− -> SpiderTimelineEnv x
2025− -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2026−localSpiderTimeline _ = unsafeCoerce
2027−
2028−-- | Pass a new timeline to the given function.
2029−withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
2030−withSpiderTimeline k = do
2031− env <- unsafeNewSpiderTimelineEnv
2032− reify env $ \s -> k $ localSpiderTimeline s env
2033−#endif
2034−
2035−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2036−
2037−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2038−
2039−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
2040−instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2041− {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2042− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2043− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2044− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2045− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2046− type PullM (SpiderTimeline x) = SpiderPullM x
2047− type PushM (SpiderTimeline x) = SpiderPushM x
2048− {-# INLINABLE never #-}
2049− never = SpiderEvent eventNever
2050− {-# INLINABLE constant #-}
2051− constant = SpiderBehavior . behaviorConst
2052− {-# INLINE push #-}
2053− push f = SpiderEvent . push (coerce f) . unSpiderEvent
2054− {-# INLINE pushCheap #-}
2055− pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2056− {-# INLINABLE pull #-}
2057− pull = SpiderBehavior . pull . coerce
2058− {-# INLINABLE merge #-}
2059− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2060− {-# INLINABLE fan #-}
1309+data Spider
1310+
1311+instance R.Reflex Spider where
1312+ newtype Behavior Spider a = SpiderBehavior { unSpiderBehavior :: Behavior a }
1313+ newtype Event Spider a = SpiderEvent { unSpiderEvent :: Event a }
1314+ type PullM Spider = BehaviorM
1315+ type PushM Spider = EventM
1316+ {-# INLINE never #-}
1317+ {-# INLINE constant #-}
1318+ never = SpiderEvent EventNever
1319+ constant = SpiderBehavior . BehaviorConst
1320+ push f = SpiderEvent. push f . unSpiderEvent
1321+ pull = SpiderBehavior . pull
1322+ merge = SpiderEvent . merge . (unsafeCoerce :: DMap k (R.Event Spider) -> DMap k Event)
20611323 fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2062− {-# INLINABLE switch #-}
2063− switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2064− {-# INLINABLE coincidence #-}
2065− coincidence = SpiderEvent . coincidence . (coerce :: Event x (R.Event (SpiderTimeline x) a) -> Event x (Event x a)) . unSpiderEvent
2066− {-# INLINABLE current #-}
2067− current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2068− {-# INLINABLE updated #-}
2069− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2070− {-# INLINABLE unsafeBuildDynamic #-}
2071− unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
2072− {-# INLINABLE unsafeBuildIncremental #-}
2073− unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
2074− {-# INLINABLE mergeIncremental #-}
2075− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2076− {-# INLINABLE mergeIncrementalWithMove #-}
2077− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2078− {-# INLINABLE currentIncremental #-}
2079− currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2080− {-# INLINABLE updatedIncremental #-}
2081− updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
2082− {-# INLINABLE incrementalToDynamic #-}
2083− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip push (dynamicUpdated i) $ \p -> do
2084− c <- readBehaviorUntracked $ dynamicCurrent i
2085− return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2086− eventCoercion Coercion = Coercion
2087− behaviorCoercion Coercion = Coercion
2088− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2089−#endif
2090−
2091−data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)
2092−
2093−data SpiderEventHandle x a = SpiderEventHandle
2094− { spiderEventHandleSubscription :: EventSubscription x
2095− , spiderEventHandleValue :: IORef (Maybe a)
2096− }
2097−
2098−instance MonadRef (EventM x) where
2099− type Ref (EventM x) = Ref IO
2100− {-# INLINABLE newRef #-}
2101− {-# INLINABLE readRef #-}
2102− {-# INLINABLE writeRef #-}
1324+ switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event Spider a) -> Behavior (Event a)) . unSpiderBehavior
1325+ coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event Spider a) -> Event (Event a)) . unSpiderEvent
1326+
1327+instance R.MonadSample Spider SpiderHost where
1328+ {-# INLINE sample #-}
1329+ sample = SpiderHost . readBehavior . unSpiderBehavior
1330+
1331+instance R.MonadHold Spider SpiderHost where
1332+ hold v0 = SpiderHost . liftM SpiderBehavior . runFrame . hold v0 . unSpiderEvent
1333+
1334+instance R.MonadSample Spider BehaviorM where
1335+ {-# INLINE sample #-}
1336+ sample = readBehaviorTracked . unSpiderBehavior
1337+
1338+instance R.MonadSample Spider EventM where
1339+ {-# INLINE sample #-}
1340+ sample = liftIO . readBehavior . unSpiderBehavior
1341+
1342+instance R.MonadHold Spider EventM where
1343+ {-# INLINE hold #-}
1344+ hold v0 e = SpiderBehavior <$> hold v0 (unSpiderEvent e)
1345+
1346+data RootTrigger a = forall k. GCompare k => RootTrigger (IORef [WeakSubscriber a], IORef (DMap k Identity), k a)
1347+newtype SpiderEventHandle a = SpiderEventHandle { unEventHandle :: Event a }
1348+
1349+instance R.MonadSubscribeEvent Spider SpiderHostFrame where
1350+ subscribeEvent e = SpiderHostFrame $ do
1351+ _ <- getEventSubscribed $ unSpiderEvent e --TODO: The result of this should actually be used
1352+ return $ SpiderEventHandle (unSpiderEvent e)
1353+
1354+instance R.ReflexHost Spider where
1355+ type EventTrigger Spider = RootTrigger
1356+ type EventHandle Spider = SpiderEventHandle
1357+ type HostFrame Spider = SpiderHostFrame
1358+
1359+instance R.MonadReadEvent Spider ReadPhase where
1360+ {-# INLINE readEvent #-}
1361+ readEvent = ReadPhase . liftM (fmap return) . readEvent . unEventHandle
1362+
1363+instance MonadRef EventM where
1364+ type Ref EventM = Ref IO
1365+ {-# INLINE newRef #-}
1366+ {-# INLINE readRef #-}
1367+ {-# INLINE writeRef #-}
21031368 newRef = liftIO . newRef
21041369 readRef = liftIO . readRef
21051370 writeRef r a = liftIO $ writeRef r a
21061371
2107−instance MonadAtomicRef (EventM x) where
2108− {-# INLINABLE atomicModifyRef #-}
1372+instance MonadAtomicRef EventM where
1373+ {-# INLINE atomicModifyRef #-}
21091374 atomicModifyRef r f = liftIO $ atomicModifyRef r f
21101375
2111−-- | The monad for actions that manipulate a Spider timeline identified by @x@
2112−newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderTimelineEnv x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2113−
2114−instance Monad (SpiderHost x) where
2115− {-# INLINABLE (>>=) #-}
2116− SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
2117− {-# INLINABLE (>>) #-}
2118− SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
2119− {-# INLINABLE return #-}
2120− return x = SpiderHost $ return x
2121− {-# INLINABLE fail #-}
2122− fail s = SpiderHost $ fail s
2123−
2124−-- | Run an action affecting the global Spider timeline; this will be guarded by
2125−-- a mutex for that timeline
2126−runSpiderHost :: SpiderHost Global a -> IO a
2127−runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimelineEnv
2128−
2129−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
2130−-- | Run an action affecting a given Spider timeline; this will be guarded by a
2131−-- mutex for that timeline
2132−runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2133−runSpiderHostForTimeline (SpiderHost a) = runReaderT a
2134−#endif
1376+newtype SpiderHost a = SpiderHost { runSpiderHost :: IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
21351377
2136−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
1378+newtype SpiderHostFrame a = SpiderHostFrame { runSpiderHostFrame :: EventM a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
21371379
2138−instance Monad (SpiderHostFrame x) where
2139− {-# INLINABLE (>>=) #-}
2140− SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
2141− {-# INLINABLE (>>) #-}
2142− SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2143− {-# INLINABLE return #-}
2144− return x = SpiderHostFrame $ return x
2145− {-# INLINABLE fail #-}
2146− fail s = SpiderHostFrame $ fail s
1380+instance R.MonadSample Spider SpiderHostFrame where
1381+ sample = SpiderHostFrame . R.sample --TODO: This can cause problems with laziness, so we should get rid of it if we can
1382+
1383+instance R.MonadHold Spider SpiderHostFrame where
1384+ {-# INLINE hold #-}
1385+ hold v0 e = SpiderHostFrame $ R.hold v0 e
21471386
2148−newEventWithTriggerIO :: (RootTrigger x a -> IO (IO ())) -> IO (Event x a)
1387+newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
21491388newEventWithTriggerIO f = do
21501389 es <- newFanEventWithTriggerIO $ \Refl -> f
21511390 return $ select es Refl
21521391
2153−newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger x a -> IO (IO ())) -> IO (EventSelector x k)
1392+newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger a -> IO (IO ())) -> IO (EventSelector k)
21541393newFanEventWithTriggerIO f = do
21551394 occRef <- newIORef DMap.empty
21561395 subscribedRef <- newIORef DMap.empty
@@ -2159,28 +1398,46 @@ newFanEventWithTriggerIO f = do
21591398 , rootSubscribed = subscribedRef
21601399 , rootInit = f
21611400 }
2162− return $ EventSelector $ \k -> eventRoot k r
2163−
2164−newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2165−
2166−instance MonadRef (SpiderHost x) where
2167− type Ref (SpiderHost x) = Ref IO
1401+ return $ EventSelector $ \k -> EventRoot k r
1402+
1403+instance R.MonadReflexCreateTrigger Spider SpiderHost where
1404+ newEventWithTrigger = SpiderHost . liftM SpiderEvent . newEventWithTriggerIO
1405+ newFanEventWithTrigger f = SpiderHost $ do
1406+ es <- newFanEventWithTriggerIO f
1407+ return $ R.EventSelector $ SpiderEvent . select es
1408+
1409+instance R.MonadReflexCreateTrigger Spider SpiderHostFrame where
1410+ newEventWithTrigger = SpiderHostFrame . EventM . liftIO . liftM SpiderEvent . newEventWithTriggerIO
1411+ newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
1412+ es <- newFanEventWithTriggerIO f
1413+ return $ R.EventSelector $ SpiderEvent . select es
1414+
1415+instance R.MonadSubscribeEvent Spider SpiderHost where
1416+ subscribeEvent e = SpiderHost $ do
1417+ _ <- runFrame $ getEventSubscribed $ unSpiderEvent e --TODO: The result of this should actually be used
1418+ return $ SpiderEventHandle (unSpiderEvent e)
1419+
1420+newtype ReadPhase a = ReadPhase { runReadPhase :: ResultM a } deriving (Functor, Applicative, Monad, MonadFix, R.MonadSample Spider, R.MonadHold Spider)
1421+
1422+instance R.MonadReflexHost Spider SpiderHost where
1423+ type ReadPhase SpiderHost = ReadPhase
1424+ fireEventsAndRead es (ReadPhase a) = SpiderHost $ run es a
1425+ runHostFrame = SpiderHost . runFrame . runSpiderHostFrame
1426+
1427+instance MonadRef SpiderHost where
1428+ type Ref SpiderHost = Ref IO
21681429 newRef = SpiderHost . newRef
21691430 readRef = SpiderHost . readRef
21701431 writeRef r = SpiderHost . writeRef r
21711432
2172−instance MonadAtomicRef (SpiderHost x) where
1433+instance MonadAtomicRef SpiderHost where
21731434 atomicModifyRef r = SpiderHost . atomicModifyRef r
21741435
2175−instance MonadRef (SpiderHostFrame x) where
2176− type Ref (SpiderHostFrame x) = Ref IO
1436+instance MonadRef SpiderHostFrame where
1437+ type Ref SpiderHostFrame = Ref IO
21771438 newRef = SpiderHostFrame . newRef
21781439 readRef = SpiderHostFrame . readRef
21791440 writeRef r = SpiderHostFrame . writeRef r
21801441
2181−instance MonadAtomicRef (SpiderHostFrame x) where
1442+instance MonadAtomicRef SpiderHostFrame where
21821443 atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2183−
2184−instance PrimMonad (SpiderHostFrame x) where
2185− type PrimState (SpiderHostFrame x) = PrimState IO
2186− primitive = SpiderHostFrame . EventM . primitive
src/Reflex/Spider/SpiderTimeline.include.hsdeleted−122
@@ -1,122 +0,0 @@
1−-- | Designates the default, global Spider timeline
2−data SpiderTimeline x
3−type role SpiderTimeline nominal
4−
5−-- | The default, global Spider environment
6−type Spider = SpiderTimeline Global
7−
8−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (EventM x) where
9− {-# INLINABLE sample #-}
10− sample (SpiderBehavior b) = readBehaviorUntracked b
11−
12−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (EventM x) where
13− {-# INLINABLE hold #-}
14− hold = holdSpiderEventM
15− {-# INLINABLE holdDyn #-}
16− holdDyn = holdDynSpiderEventM
17− {-# INLINABLE holdIncremental #-}
18− holdIncremental = holdIncrementalSpiderEventM
19−
20−instance (
21−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
22− x ~ Global
23−#endif
24− ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
25− {-# INLINABLE sample #-}
26− sample = coerce . readBehaviorTracked . unSpiderBehavior
27−
28−instance (
29−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
30− x ~ Global
31−#endif
32− ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPushM x) where
33− {-# INLINABLE sample #-}
34− sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
35−
36−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderPushM x) where
37− {-# INLINABLE hold #-}
38− hold v0 e = Reflex.Class.current <$> Reflex.Class.holdDyn v0 e
39− {-# INLINABLE holdDyn #-}
40− holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce e
41− {-# INLINABLE holdIncremental #-}
42− holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . dynamicHold <$> Reflex.Spider.Internal.hold v0 e
43−
44−instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
45− {-# INLINE return #-}
46− return = pure
47− {-# INLINE (>>=) #-}
48− x >>= f = SpiderDynamic $ dynamicDynIdentity $ newJoinDyn $ newMapDyn (unSpiderDynamic . f) $ unSpiderDynamic x
49− {-# INLINE (>>) #-}
50− _ >> y = y
51− {-# INLINE fail #-}
52− fail _ = error "Dynamic does not support 'fail'"
53−
54−newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
55−newJoinDyn d =
56− let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
57− eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
58− eInner = Reflex.Spider.Internal.switch $ dynamicUpdated <$> dynamicCurrent d
59− eBoth = Reflex.Spider.Internal.coincidence $ dynamicUpdated . runIdentity <$> dynamicUpdated d
60− v' = unSpiderEvent $ Reflex.Class.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
61− in unsafeDyn readV0 v'
62−
63−instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
64− fmap f = SpiderDynamic . newMapDyn f . unSpiderDynamic
65−
66−instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
67− pure = SpiderDynamic . dynamicConst
68− SpiderDynamic a <*> SpiderDynamic b = SpiderDynamic $ Reflex.Spider.Internal.zipDynWith ($) a b
69− _ *> b = b
70− a <* _ = a
71−
72−holdSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Behavior (SpiderTimeline x) a)
73−holdSpiderEventM v0 e = fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
74−
75−holdDynSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Dynamic (SpiderTimeline x) a)
76−holdDynSpiderEventM v0 e = fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
77−
78−holdIncrementalSpiderEventM :: (HasSpiderTimeline x, Patch p) => PatchTarget p -> Reflex.Class.Event (SpiderTimeline x) p -> EventM x (Reflex.Class.Incremental (SpiderTimeline x) p)
79−holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
80−
81−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHost x) where
82− {-# INLINABLE hold #-}
83− hold v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.hold v0 e
84− {-# INLINABLE holdDyn #-}
85− holdDyn v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdDyn v0 e
86− {-# INLINABLE holdIncremental #-}
87− holdIncremental v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdIncremental v0 e
88−
89−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
90− sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
91−
92−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHostFrame x) where
93− {-# INLINABLE hold #-}
94− hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
95− {-# INLINABLE holdDyn #-}
96− holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
97− {-# INLINABLE holdIncremental #-}
98− holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
99−
100−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
101− {-# INLINABLE sample #-}
102− sample = runFrame . readBehaviorUntracked . unSpiderBehavior
103−
104−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
105− {-# INLINABLE sample #-}
106− sample = Reflex.Spider.Internal.ReadPhase . Reflex.Class.sample
107−
108−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
109− {-# INLINABLE hold #-}
110− hold v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.hold v0 e
111− {-# INLINABLE holdDyn #-}
112− holdDyn v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdDyn v0 e
113− {-# INLINABLE holdIncremental #-}
114− holdIncremental v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdIncremental v0 e
115−
116−--------------------------------------------------------------------------------
117−-- Deprecated items
118−--------------------------------------------------------------------------------
119−
120−-- | 'SpiderEnv' is the old name for 'SpiderTimeline'
121−{-# DEPRECATED SpiderEnv "Use 'SpiderTimelineEnv' instead" #-}
122−type SpiderEnv = SpiderTimeline
src/Reflex/Spider/SpiderTimelineHost.include.hsdeleted−57
@@ -1,57 +0,0 @@
1−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHostFrame x) where
2− {-# INLINABLE subscribeEvent #-}
3− subscribeEvent e = SpiderHostFrame $ do
4− --TODO: Unsubscribe eventually (manually and/or with weak ref)
5− val <- liftIO $ newIORef Nothing
6− subscription <- subscribe (unSpiderEvent e) $ Subscriber
7− { subscriberPropagate = \a -> do
8− liftIO $ writeIORef val $ Just a
9− scheduleClear val
10− , subscriberInvalidateHeight = \_ -> return ()
11− , subscriberRecalculateHeight = \_ -> return ()
12− }
13− return $ SpiderEventHandle
14− { spiderEventHandleSubscription = subscription
15− , spiderEventHandleValue = val
16− }
17−
18−instance HasSpiderTimeline x => Reflex.Host.Class.ReflexHost (SpiderTimeline x) where
19− type EventTrigger (SpiderTimeline x) = RootTrigger x
20− type EventHandle (SpiderTimeline x) = SpiderEventHandle x
21− type HostFrame (SpiderTimeline x) = SpiderHostFrame x
22−
23−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReadEvent (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
24− {-# NOINLINE readEvent #-}
25− readEvent h = Reflex.Spider.Internal.ReadPhase $ fmap (fmap return) $ liftIO $ do
26− result <- readIORef $ spiderEventHandleValue h
27− touch h
28− return result
29−
30−instance (
31−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
32− x ~ Global
33−#endif
34− ) => Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHost x) where
35− newEventWithTrigger = SpiderHost . lift . fmap SpiderEvent . newEventWithTriggerIO
36− newFanEventWithTrigger f = SpiderHost $ lift $ do
37− es <- newFanEventWithTriggerIO f
38− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
39−
40−instance (
41−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
42− x ~ Global
43−#endif
44− ) => Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHostFrame x) where
45− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
46− newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
47− es <- newFanEventWithTriggerIO f
48− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
49−
50−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHost x) where
51− {-# INLINABLE subscribeEvent #-}
52− subscribeEvent = runFrame . runSpiderHostFrame . Reflex.Host.Class.subscribeEvent
53−
54−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReflexHost (SpiderTimeline x) (SpiderHost x) where
55− type ReadPhase (SpiderHost x) = Reflex.Spider.Internal.ReadPhase x
56− fireEventsAndRead es (Reflex.Spider.Internal.ReadPhase a) = run es a
57− runHostFrame = runFrame . runSpiderHostFrame
src/Reflex/TriggerEvent/Base.hsdeleted−111
@@ -1,111 +0,0 @@
1−-- | This module defines 'TriggerEventT', the standard implementation of
2−-- 'TriggerEvent'.
3−{-# LANGUAGE DeriveFunctor #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE TypeFamilies #-}
8−module Reflex.TriggerEvent.Base
9− ( TriggerEventT (..)
10− , runTriggerEventT
11− , askEvents
12− , TriggerInvocation (..)
13− , EventTriggerRef (..)
14− ) where
15−
16−import Control.Concurrent
17−import Control.Monad.Exception
18−import Control.Monad.Reader
19−import Control.Monad.Ref
20−import Data.Dependent.Sum
21−import Reflex.Class
22−import Reflex.Host.Class
23−import Reflex.PerformEvent.Class
24−import Reflex.PostBuild.Class
25−import Reflex.TriggerEvent.Class
26−
27−-- | A value with which to fire an 'Event', as well as a callback to invoke
28−-- after its propagation has completed.
29−data TriggerInvocation a = TriggerInvocation a (IO ())
30−
31−-- | A reference to an 'EventTrigger' suitable for firing with 'TriggerEventT'.
32−newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
33−
34−-- | A basic implementation of 'TriggerEvent'.
35−newtype TriggerEventT t m a = TriggerEventT { unTriggerEventT :: ReaderT (Chan [DSum (EventTriggerRef t m) TriggerInvocation]) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
36−
37−-- | Run a 'TriggerEventT' action. The argument should be a 'Chan' into which
38−-- 'TriggerInvocation's can be passed; it is expected that some other thread
39−-- will be responsible for popping values out of the 'Chan' and firing their
40−-- 'EventTrigger's.
41−runTriggerEventT :: TriggerEventT t m a -> Chan [DSum (EventTriggerRef t m) TriggerInvocation] -> m a
42−runTriggerEventT = runReaderT . unTriggerEventT
43−
44−instance MonadTrans (TriggerEventT t) where
45− lift = TriggerEventT . lift
46−
47−instance PerformEvent t m => PerformEvent t (TriggerEventT t m) where
48− type Performable (TriggerEventT t m) = Performable m
49− {-# INLINABLE performEvent_ #-}
50− performEvent_ e = lift $ performEvent_ e
51− {-# INLINABLE performEvent #-}
52− performEvent e = lift $ performEvent e
53−
54−instance PostBuild t m => PostBuild t (TriggerEventT t m) where
55− {-# INLINABLE getPostBuild #-}
56− getPostBuild = lift getPostBuild
57−
58−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (TriggerEventT t m) where
59− {-# INLINABLE newEventWithTrigger #-}
60− newEventWithTrigger = lift . newEventWithTrigger
61− {-# INLINABLE newFanEventWithTrigger #-}
62− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
63−
64−instance (Monad m, MonadRef m, Ref m ~ Ref IO, MonadReflexCreateTrigger t m) => TriggerEvent t (TriggerEventT t m) where
65− {-# INLINABLE newTriggerEvent #-}
66− newTriggerEvent = do
67− (e, t) <- newTriggerEventWithOnComplete
68− return (e, \a -> t a $ return ())
69− {-# INLINABLE newTriggerEventWithOnComplete #-}
70− newTriggerEventWithOnComplete = do
71− events <- askEvents
72− (eResult, reResultTrigger) <- lift newEventWithTriggerRef
73− return . (,) eResult $ \a cb ->
74− writeChan events [EventTriggerRef reResultTrigger :=> TriggerInvocation a cb]
75− {-# INLINABLE newEventWithLazyTriggerWithOnComplete #-}
76− newEventWithLazyTriggerWithOnComplete f = do
77− events <- askEvents
78− lift . newEventWithTrigger $ \t ->
79− f $ \a cb -> do
80− reResultTrigger <- newRef $ Just t
81− writeChan events [EventTriggerRef reResultTrigger :=> TriggerInvocation a cb]
82−
83−instance MonadRef m => MonadRef (TriggerEventT t m) where
84− type Ref (TriggerEventT t m) = Ref m
85− {-# INLINABLE newRef #-}
86− newRef = lift . newRef
87− {-# INLINABLE readRef #-}
88− readRef = lift . readRef
89− {-# INLINABLE writeRef #-}
90− writeRef r = lift . writeRef r
91−
92−instance MonadAtomicRef m => MonadAtomicRef (TriggerEventT t m) where
93− {-# INLINABLE atomicModifyRef #-}
94− atomicModifyRef r = lift . atomicModifyRef r
95−
96−instance MonadSample t m => MonadSample t (TriggerEventT t m) where
97− {-# INLINABLE sample #-}
98− sample = lift . sample
99−
100−instance MonadHold t m => MonadHold t (TriggerEventT t m) where
101− {-# INLINABLE hold #-}
102− hold v0 v' = lift $ hold v0 v'
103− {-# INLINABLE holdDyn #-}
104− holdDyn v0 v' = lift $ holdDyn v0 v'
105− {-# INLINABLE holdIncremental #-}
106− holdIncremental v0 v' = lift $ holdIncremental v0 v'
107−
108−-- | Retrieve the current 'Chan'; event trigger invocations pushed into it will
109−-- be fired.
110−askEvents :: Monad m => TriggerEventT t m (Chan [DSum (EventTriggerRef t m) TriggerInvocation])
111−askEvents = TriggerEventT ask
src/Reflex/TriggerEvent/Class.hsdeleted−38
@@ -1,38 +0,0 @@
1−-- | This module defines 'TriggerEvent', which describes actions that may create
2−-- new 'Event's that can be triggered from 'IO'.
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE UndecidableInstances #-}
7−module Reflex.TriggerEvent.Class
8− ( TriggerEvent (..)
9− ) where
10−
11−import Reflex.Class
12−
13−import Control.Monad.Reader
14−
15−--TODO: Shouldn't have IO hard-coded
16−-- | 'TriggerEvent' represents actions that can create 'Event's that can be
17−-- triggered by 'IO' actions.
18−class Monad m => TriggerEvent t m | m -> t where
19− -- | Create a triggerable 'Event'. Whenever the resulting function is called,
20− -- the resulting 'Event' will fire at some point in the future. Note that
21− -- this may not be synchronous.
22− newTriggerEvent :: m (Event t a, a -> IO ())
23− -- | Like 'newTriggerEvent', but the callback itself takes another callback,
24− -- to be invoked once the requested 'Event' occurrence has finished firing.
25− -- This allows synchronous operation.
26− newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
27− -- | Like 'newTriggerEventWithOnComplete', but with setup and teardown. This
28− -- relatively complex type signature allows any external listeners to be
29− -- subscribed lazily and then removed whenever the returned 'Event' is no
30− -- longer being listened to. Note that the setup/teardown may happen multiple
31− -- times, and there is no guarantee that the teardown will be executed
32− -- promptly, or even at all, in the case of program termination.
33− newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
34−
35−instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
36− newTriggerEvent = lift newTriggerEvent
37− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
38− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
test/EventWriterT.hsdeleted−61
@@ -1,61 +0,0 @@
1−{-# LANGUAGE FlexibleContexts, GADTs #-}
2−module Main where
3−
4−import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
7−import Data.Dependent.Sum
8−import Data.IORef
9−import Data.List
10−import Data.Functor.Identity
11−
12−import Reflex
13−import Reflex.EventWriter
14−import Reflex.Host.Class
15−import Reflex.PerformEvent.Base
16−
17−import System.Exit (exitFailure)
18−
19−main :: IO ()
20−main = do
21− (Just out1, Nothing) <- runApp app1
22− print out1
23− (Just out2, Just (out3, out4)) <- runApp app2
24− print out2
25− print out3
26− print out4
27− when (sort out1 /= out1 || map (+1) out2 /= out3 || out3 /= out4) exitFailure
28−
29−runApp :: (Show a, t ~ SpiderTimeline Global, m ~ SpiderHost Global)
30− => (Event t () -> EventWriterT t [a] (PerformEventT t m) (Maybe (Ref m (Maybe (EventTrigger t ())))))
31− -> IO (Maybe [a], Maybe ([a], [a]))
32−runApp x = runSpiderHost $ do
33− (pulseE, pulseTriggerRef) <- newEventWithTriggerRef
34− ((mactuateRef, e), FireCommand fire) <- hostPerformEventT $ runEventWriterT (x pulseE)
35− hnd <- subscribeEvent e
36− out1 <- fireEventRefAndRead pulseTriggerRef () hnd
37− out2 <- fmap join $ forM mactuateRef $ \actuateRef -> do
38− let readPhase = do
39− mGetValue <- readEvent hnd
40− case mGetValue of
41− Nothing -> return Nothing
42− Just getValue -> fmap Just getValue
43− mactuate <- readRef actuateRef
44− mpulse <- readRef pulseTriggerRef
45− forM ((,) <$> mactuate <*> mpulse) $ \(actuate, pulse) -> do
46− _ <- fire [actuate :=> Identity ()] $ return ()
47− [Just a] <- fire [pulse :=> Identity ()] $ readPhase
48− [Just b, Nothing] <- fire [actuate :=> Identity (), pulse :=> Identity ()] $ readPhase
49− return (a,b)
50− return (out1, out2)
51−
52−app1 :: (Reflex t, Ref m ~ IORef, EventWriter t [Int] m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
53−app1 e = do
54− forM_ [1..10] $ \i -> tellEvent ([i] <$ e)
55− return Nothing
56−
57−app2 :: (Reflex t, Ref m ~ IORef, EventWriter t [Int] m, MonadAdjust t m, MonadReflexCreateTrigger t m, MonadRef m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
58−app2 e = do
59− (pulse, pulseTriggerRef) <- newEventWithTriggerRef
60− forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e)) $ ffor pulse $ \_ -> tellEvent ([i+1] <$ e)
61− return (Just pulseTriggerRef)
test/GC.hsdeleted−95
@@ -1,95 +0,0 @@
1−{-# LANGUAGE OverloadedStrings, FlexibleContexts, GADTs, TypeFamilies, RankNTypes, ScopedTypeVariables, LambdaCase #-}
2−module Main where
3−
4−import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
7−import Data.Align
8−import Data.Dependent.Map (DMap)
9−import qualified Data.Dependent.Map as DMap
10−import Data.Dependent.Sum
11−import Data.Functor.Identity
12−import Data.GADT.Compare
13−import Data.IORef
14−import Data.Semigroup
15−import Data.These
16−
17−import Data.Functor.Misc
18−import Reflex.Patch
19−
20−import qualified Reflex.Spider.Internal as S
21−import qualified Reflex.Host.Class as Host
22−
23−import System.Mem
24−import System.Exit
25−
26−main :: IO ()
27−main = do
28− ref <- newIORef Nothing
29− hostPerf ref
30− readIORef ref >>= maybe exitFailure (putStrLn . ("Result " <>) . show)
31−
32−hostPerf :: IORef (Maybe Int) -> IO ()
33−hostPerf ref = S.runSpiderHost $ do
34− ---- body
35− liftIO $ putStrLn "#creating triggers"
36− (response', responseTrigger) <- Host.newEventWithTriggerRef
37− (eadd', addTriggerRef) <- Host.newEventWithTriggerRef
38− let response = S.unSpiderEvent response'
39− eadd = S.unSpiderEvent eadd'
40− liftIO $ putStrLn "#creating event graph"
41− eventToPerform <- Host.runHostFrame $ do
42− (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
43− <- S.SpiderHostFrame
44− $ fmap ( S.merge
45− . S.dynamicHold)
46− $ S.hold DMap.empty
47− -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
48− $ (\e -> S.Event $ \sub -> do
49− (s, o) <- S.subscribeAndRead e $ sub
50− { S.subscriberPropagate = S.subscriberPropagate sub
51− }
52− return (s, o))
53− $ runIdentity <$> S.select
54− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
55− (WrapArg Request)
56− return $ alignWith (mergeThese (<>))
57− (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
58− liftIO $ putStrLn "#eadd fired"
59− return $ PatchDMap $ DMap.singleton (Const2 (1 :: Int)) $ ComposeMaybe $ Just
60− $ S.pushCheap (return . Just . DMap.singleton Action . (\_ -> liftIO (writeIORef ref (Just 1)))) $ eadd)
61− (flip S.pushCheap reqMap $ \m -> return $ Just $ mconcat $ (\(Const2 _ :=> Identity reqs) -> reqs) <$> DMap.toList m)
62− ---- epilogue
63− eventToPerformHandle <- Host.subscribeEvent (S.SpiderEvent eventToPerform)
64− liftIO $ putStrLn "#performing GC" >> performMajorGC
65− liftIO $ putStrLn "#attempting to fire eadd"
66− mAddTrigger <- readRef addTriggerRef
67− forM_ mAddTrigger $ \t -> replicateM_ 2 $ do
68− liftIO $ putStrLn "#firing eadd"
69− mToPerform <- Host.fireEventsAndRead [t :=> Identity ()] $ sequence =<< Host.readEvent eventToPerformHandle
70− case mToPerform of
71− Nothing -> return ()
72− Just toPerform -> do
73− responses <- Host.runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
74− mrt <- readRef responseTrigger
75− let followupEventTriggers = case mrt of
76− Just rt -> [rt :=> Identity responses]
77− Nothing -> []
78− Host.fireEventsAndRead followupEventTriggers $ return ()
79− return ()
80− return ()
81−
82−data Tell a where
83− Action :: Tell ()
84− Request :: Tell (PatchDMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) (S.Event S.Global))
85−
86−instance GEq Tell where
87− geq Action Action = Just Refl
88− geq Request Request = Just Refl
89− geq _ _ = Nothing
90−
91−instance GCompare Tell where
92− gcompare Action Action = GEQ
93− gcompare Request Request = GEQ
94− gcompare Action Request = GLT
95− gcompare Request Action = GGT
test/Reflex/Bench/Focused.hs+54−150
@@ -1,43 +1,25 @@
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 #-}
1+{-# LANGUAGE RecursiveDo, ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
112
123module Reflex.Bench.Focused where
134
145import Reflex
156import Reflex.TestPlan
167
17−import Control.Applicative
18−import Control.Monad
198import Control.Monad.Fix
20−import Control.Monad.Identity
21−import Data.Traversable (for, traverse)
9+import Control.Monad
10+import Control.Applicative
11+import Data.Foldable
12+import Data.Traversable
2213
23−import qualified Data.Dependent.Map as DMap
14+import Data.Maybe
2415import Data.Map (Map)
2516import qualified Data.Map as Map
26−import Data.Maybe
27−
28−import Data.Functor.Misc
2917
18+import Data.Tuple
3019import Data.List
3120import 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)
4021
22+import Prelude
4123
4224mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
4325mergeTree n es | length es <= n = sum' es
@@ -47,17 +29,15 @@ mergeTree n es | length es <= n = sum' es
4729 subTrees = map sum' merges
4830 sum' = fmap sum . mconcat . fmap (fmap (:[]) )
4931
50−mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
51−mergeListDyn = mconcat . fmap (fmap pure)
32+mergeListDyn :: (Reflex t, MonadHold t m) => [Dynamic t a] -> m (Dynamic t [a])
33+mergeListDyn dyns = traverse (mapDyn pure) dyns >>= mconcatDyn
5234
53−mergeTreeDyn :: (Num a, Reflex t) => Int -> [Dynamic t a] -> Dynamic t a
35+mergeTreeDyn :: (Num a, Reflex t, MonadHold t m, MonadFix m) => Int -> [Dynamic t a] -> m (Dynamic t a)
5436mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
55− | otherwise = mergeTreeDyn n $ fmap sumDyns merges
37+ | otherwise = traverse sumDyns merges >>= mergeTreeDyn n
5638 where
57− merges = chunksOf n dyns
58− sumDyns = fmap sum . mergeListDyn
59−
60−
39+ merges = chunksOf n dyns
40+ sumDyns ds = mergeListDyn ds >>= mapDyn sum
6141
6242-- N events all firing for N frames
6343continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
@@ -70,13 +50,10 @@ occasional n period frames = traverse plan $ zipWith occs [1..] phases
7050 phases = take (fromIntegral n) $ concat (repeat [1..period])
7151 occs i phase = (, i) <$> [1, phase+1..frames]
7252
73−
74−
7553-- Event which never fires (but isn't 'never')
7654never' :: TestPlan t m => m (Event t Word)
7755never' = plan []
7856
79−
8057-- Event which fires once on first frame
8158event :: TestPlan t m => m (Event t Word)
8259event = plan [(1, 0)]
@@ -85,53 +62,30 @@ event = plan [(1, 0)]
8562events :: TestPlan t m => Word -> m (Event t Word)
8663events n = plan $ (\i -> (i, i)) <$> [1..n]
8764
88−
8965-- N events all originating from one event
9066fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
9167fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
9268
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−
11169-- Create an Event with a Map which is dense to size N
11270denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
11371denseMap n = plan [(1, m)] where
11472 m = Map.fromList $ zip [1..n] [1..n]
11573
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))
74+fmapChain :: Reflex t => Word -> Event t Word -> (Event t Word)
75+fmapChain 0 e = e
76+fmapChain n e = fmapChain (n - 1) (fmap (+1) e)
12677
12778mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
128−mapDynChain = iterM (return . fmap (+1))
79+mapDynChain 0 d = return d
80+mapDynChain n d = mapDynChain (n - 1) =<< mapDyn (+1) d
12981
13082joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131−joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
83+joinDynChain 0 d = return d
84+joinDynChain n d = joinDynChain (n - 1) =<< joinDyn <$> mapDyn (const d) d
13285
13386combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134−combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
87+combineDynChain 0 d = return d
88+combineDynChain n d = combineDynChain (n - 1) =<< combineDyn (+) d d
13589
13690pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
13791pingPong e1 e2 = do
@@ -146,27 +100,27 @@ switchFactors i e = do
146100 b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
147101 switchFactors (i - 1) (switch b)
148102
149−
150103switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
151−switchChain = iterM (\e -> pingPong e ((+1) <$> e))
152−
104+switchChain 0 e = return e
105+switchChain i e = switchChain (i - 1) =<< pingPong e ((+1) <$> e)
153106
154107switchChain2 :: (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]))
108+switchChain2 0 e = return e
109+switchChain2 i e = switchChain2 (i - 1) =<< pingPong e ((+1) <$> leftmost [e, e])
156110
157111switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
158−switchPromptlyChain = iterM $ \e ->
159− switchPromptlyDyn <$> holdDyn e (e <$ e)
160−
112+switchPromptlyChain 0 e = return e
113+switchPromptlyChain i e = do
114+ d <- holdDyn e (e <$ e)
115+ switchPromptlyChain (i - 1) (switchPromptlyDyn d)
161116
162117coinChain :: Reflex t => Word -> Event t Word -> Event t Word
163−coinChain = iter $ \e -> coincidence (e <$ e)
118+coinChain 0 e = e
119+coinChain n e = coinChain (n - 1) $ coincidence (e <$ e)
164120
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
121+pullChain :: Reflex t => Word -> Behavior t Word -> Behavior t Word
122+pullChain 0 b = b
123+pullChain n b = pullChain (n - 1) $ (+1) <$> b
170124
171125-- | Data type for a collection (a map) which is updated by providing differences
172126data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
@@ -175,17 +129,13 @@ data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
175129switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
176130switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
177131
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−
181132switchMergeBehaviors :: 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
133+switchMergeBehaviors mapChanges = pull <$> joinMap <$> holdMap mapChanges
183134 where joinMap m = traverse sample =<< sample m
184135
185136-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
186137holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
187138holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)) initial changes
188−
189139 where
190140 modify k Nothing items = Map.delete k items
191141 modify k (Just item) items = Map.insert k item items
@@ -195,7 +145,6 @@ holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)
195145holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
196146holdMap = (current <$>) . holdMapDyn
197147
198−
199148increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
200149increasingMerge xs = do
201150 e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
@@ -218,43 +167,34 @@ modifyMerge xs = do
218167 where
219168 initial = Map.fromList $ zip [1..] xs
220169
221−
222170countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
223171countMany = traverse (fmap current . count)
224172
225173countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
226174countManyDyn = traverse count
227175
228−
229176-- Given a function which creates a sub-network (from a single event)
230177-- Create a plan which uses a switch to substitute it
231178switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
232179switches numFrames f = do
233180 es <- events numFrames
234181 switch <$> hold never (makeEvents es)
235−
236182 where
237183 makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
238184
239−
240−
241185-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
242186-- the other, which we're only interested in time for running frames
243187subscribing :: Word -> Word -> [(String, TestCase)]
244188subscribing 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
189+ [ testE "fmapFan merge" $ switches frames (return . mergeList . fmapFan n)
190+ , testE "fmapFan/mergeTree" $ switches frames (return . mergeTree 8 . fmapFan n)
191+ , testE "fmapChain" $ switches frames (return . fmapChain n)
192+ , testE "switchChain" $ switches frames (switchChain n)
193+ , testE "switchPromptlyChain" $ switches frames (switchPromptlyChain n)
194+ , testE "switchFactors" $ switches frames (switchFactors n)
195+ , testE "coincidenceChain" $ switches frames (return . coinChain n)
253196 ]
254197
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)
258198
259199-- Set of benchmark to test specifically merging dynamic collections
260200-- a pattern which occurs frequently in reflex-dom collections
@@ -269,11 +209,6 @@ merging n =
269209 , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
270210 , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
271211 , 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
277212 ]
278213
279214 where
@@ -283,37 +218,15 @@ merging n =
283218 counters :: TestPlan t m => m [Behavior t Int]
284219 counters = countMany =<< sparse
285220
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−
307221dynamics :: Word -> [(String, TestCase)]
308222dynamics n =
309223 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310224 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
311225 , 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
226+ , testE "dense mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< dense
227+ , testE "sparse mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< sparse
228+ , testE "mergeDyn" $ fmap updated $ mapDyn sum =<< mergeListDyn =<< sparse
315229 ] where
316−
317230 d :: TestPlan t m => m (Dynamic t Word)
318231 d = count =<< events 10
319232
@@ -332,25 +245,20 @@ firing n =
332245
333246 , testE "dense mergeTree" $ mergeTree 8 <$> dense
334247 , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
335−
336− -- Triggers test failure in Spider
337− --, testE "runFrame" $ events n
338−
248+ , testE "runFrame" $ events n
339249 , testB "sum counters" $ do
340250 counts <- counters
341251 return $ pull $ sum <$> traverse sample counts
342252
343− , testB "pullChain" $ fmapChain n . current <$> (count =<< events 4)
253+ , testB "pullChain" $ pullChain n . current <$> (count =<< events 4)
344254 , 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−
255+ e' <- events 4
256+ b <- hold (constant 0) $ leftmost
257+ [ constant 0 <$ ffilter (==4) e'
258+ , pushAlways (const $ pullChain n <$> hold 0 e') e'
259+ ]
351260 return (join b)
352261 , testB "counters/pullTree" $ mergeTree 8 <$> counters
353−
354262 ]
355263 where
356264 e :: TestPlan t m => m (Event t Word)
@@ -363,7 +271,3 @@ firing n =
363271 counters :: TestPlan t m => m [Behavior t Int]
364272 counters = countMany =<< sparse
365273
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.hs+16−30
@@ -1,24 +1,14 @@
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 #-}
1+{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
112module Reflex.Plan.Reflex
123 ( TestPlan(..)
134 , runPlan
14− , Plan (..)
5+ , Plan
156 , Schedule
167 , Firing (..)
178 , MonadIORef
189
1910
2011 , readSchedule
21− , readSchedule_
2212 , testSchedule
2313 , readEvent'
2414 , makeDense
@@ -37,15 +27,13 @@ import Control.Monad
3727import Control.Monad.Identity
3828import Control.Monad.State.Strict
3929
40−import Control.Monad.Ref
4130import Data.Dependent.Sum (DSum (..))
42−import qualified Data.IntMap.Strict as IntMap
43−import Data.Maybe
4431import Data.Monoid
45−import Data.Traversable (sequenceA, traverse)
32+import Data.Maybe
33+import qualified Data.IntMap.Strict as IntMap
34+import Control.Monad.Ref
35+
4636
47−import Control.DeepSeq
48−import Control.Exception
4937import Data.IntMap.Strict (IntMap)
5038import Data.IORef
5139import System.Mem
@@ -87,7 +75,7 @@ instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => T
8775firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
8876firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
8977
90−runPlan :: (MonadReflexHost t m) => Plan t a -> m (a, Schedule t)
78+runPlan :: (MonadReflexHost t m, MonadIORef m) => Plan t a -> m (a, Schedule t)
9179runPlan (Plan p) = runHostFrame $ runStateT p mempty
9280
9381
@@ -102,31 +90,29 @@ makeDense s = fromMaybe (emptyRange 0) $ do
10290-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
10391-- after the last event)
10492-- 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)
93+testSchedule :: (MonadReflexHost t m, MonadIORef m) => Schedule t -> ReadPhase m a -> m (IntMap a)
10694testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
10795
108−readSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
96+readSchedule :: (MonadReflexHost t m, MonadIORef m) => Schedule t -> ReadPhase m a -> m (IntMap a)
10997readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
11098
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
99+triggerFrame :: (MonadReflexHost t m, MonadIORef m) => ReadPhase m a -> Int -> [Firing t] -> m a
115100triggerFrame readResult _ occs = do
116− triggers <- catMaybes <$> traverse firingTrigger occs
117− liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
101+ triggers <- {-# SCC "catMaybes/traverse" #-} catMaybes <$> traverse firingTrigger occs
102+ {-# SCC fireEventsAndRead #-} fireEventsAndRead triggers readResult
103+
118104
119105readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
120−readEvent' = readEvent >=> sequenceA
106+readEvent' = readEvent >=> sequence
121107
122108
123109-- 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)
110+runTestB :: (MonadReflexHost t m, MonadIORef m) => Plan t (Behavior t a) -> m (IntMap a)
125111runTestB p = do
126112 (b, s) <- runPlan p
127113 testSchedule s $ sample b
128114
129−runTestE :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Event t a) -> m (IntMap (Maybe a))
115+runTestE :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (IntMap (Maybe a))
130116runTestE p = do
131117 (e, s) <- runPlan p
132118 h <- subscribeEvent e
test/Reflex/Pure.hsnew file+76
@@ -0,0 +1,76 @@
1+{- | This module provides a pure implementation of Reflex, which is intended
2+to serve as a reference for the semantics of the Reflex class. All implementations
3+of Reflex should produce the same results as this implementation, although performance
4+and laziness/strictness may differ.
5+-}
6+
7+{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, EmptyDataDecls, InstanceSigs #-}
8+module Reflex.Pure where
9+
10+import Reflex.Class
11+import Data.Functor.Misc
12+import Control.Monad.Identity
13+import Control.Monad
14+import Data.MemoTrie
15+import Data.Dependent.Map (DMap, GCompare)
16+import qualified Data.Dependent.Map as DMap
17+
18+data Pure t
19+
20+-- | The Enum instance of t must be dense: for all x :: t, there must not exist
21+-- any y :: t such that pred x < y < x. The HasTrie instance will be used exclusively
22+-- to memoize functions of t, not for any of its other capabilities.
23+instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
24+
25+ newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
26+ newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
27+
28+ type PushM (Pure t) = (->) t
29+ type PullM (Pure t) = (->) t
30+
31+ never :: Event (Pure t) a
32+ never = Event $ \_ -> Nothing
33+
34+ constant :: a -> Behavior (Pure t) a
35+ constant x = Behavior $ \_ -> x
36+
37+ push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
38+ push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
39+
40+ pull :: PullM (Pure t) a -> Behavior (Pure t) a
41+ pull = Behavior . memo
42+
43+ merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
44+ merge events = Event $ memo $ \t ->
45+ let currentOccurrences = unwrapDMapMaybe (($ t) . unEvent) events
46+ in if DMap.null currentOccurrences
47+ then Nothing
48+ else Just currentOccurrences
49+
50+ fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
51+ fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
52+
53+ switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
54+ switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
55+
56+ coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
57+ coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
58+
59+instance Ord t => MonadSample (Pure t) ((->) t) where
60+
61+ sample :: Behavior (Pure t) a -> (t -> a)
62+ sample = unBehavior
63+
64+instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
65+
66+ hold :: a -> Event (Pure t) a -> (t -> (Behavior (Pure t) a))
67+ hold initialValue e initialTime = Behavior f
68+ where f = memo $ \sampleTime ->
69+ -- Really, the sampleTime should never be prior to the initialTime,
70+ -- because that would mean the Behavior is being sampled before being created.
71+ if sampleTime <= initialTime
72+ then initialValue
73+ else let lastTime = pred sampleTime
74+ in case unEvent e lastTime of
75+ Nothing -> f lastTime
76+ Just x -> x
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.hs+29−45
@@ -1,40 +1,27 @@
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 #-}
1+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, BangPatterns, ConstraintKinds #-}
152module Reflex.Test.CrossImpl (test) where
163
17−import Prelude hiding (and, foldl, mapM, mapM_, sequence, sequence_)
4+import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl, and)
185
19−import Control.Monad.Ref
206import Reflex.Class
21−import Reflex.Dynamic
227import Reflex.Host.Class
23−import qualified Reflex.Pure as P
8+import Reflex.Dynamic
249import qualified Reflex.Spider.Internal as S
10+import qualified Reflex.Pure as P
11+import Control.Monad.Ref
2512
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
13+import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
14+import qualified Data.Set as Set
3215import Data.Map.Strict (Map)
3316import qualified Data.Map.Strict as Map
34−import qualified Data.Set as Set
17+import Control.Arrow (second, (&&&))
3518import Data.Traversable
36−import System.Exit
19+import Data.Foldable
20+import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
21+import Control.Monad.Writer hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
22+import Data.Dependent.Map (DSum (..))
3723import System.Mem
24+import System.Exit
3825
3926import System.IO.Unsafe
4027
@@ -86,7 +73,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
8673 outputs <- forM times $ \t -> do
8774 forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
8875 bOutput <- sample b'
89− eOutput <- fmap join $ forM (Map.lookup t eMap) $ \val -> do
76+ eOutput <- liftM join $ forM (Map.lookup t eMap) $ \val -> do
9077 mret <- readRef reTrigger
9178 let firing = case mret of
9279 Just ret -> [ret :=> Identity val]
@@ -94,7 +81,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
9481 fireEventsAndRead firing $ sequence =<< readEvent e'Handle
9582 liftIO performGC
9683 return (t, (bOutput, eOutput))
97− return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
84+ return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
9885
9986tracePerf :: Show a => a -> b -> b
10087tracePerf = flip const
@@ -124,7 +111,7 @@ testCases =
124111 b' <- hold "123" e
125112 return (b', e)
126113 , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
127− e' <- updated <$> count e
114+ e' <- liftM updated $ count e
128115 b' <- hold (0 :: Int) e'
129116 return (b', e')
130117 , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -137,19 +124,23 @@ testCases =
137124 return (b, e'')
138125 , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
139126 let e' = flip pushAlways e $ const $ do
140− let eab = splitRecombineEvent e
141− switch <$> hold eab never
127+ let ea = fmap (const "a") e
128+ let eb = fmap (const "b") e
129+ let eab = leftmost [ea, eb]
130+ liftM switch $ hold eab never
142131 e'' = coincidence e'
143132 return (b, e'')
144133 , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
145134 let e' = flip pushAlways e $ const $ do
146− let eab = splitRecombineEvent e
147− switch <$> hold eab (fmap (const e) e)
135+ let ea = fmap (const "a") e
136+ let eb = fmap (const "b") e
137+ let eab = leftmost [ea, eb]
138+ liftM switch $ hold eab (fmap (const e) e)
148139 e'' = coincidence e'
149140 return (b, e'')
150141 , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
151142 let e' = leftmost [e, e]
152− e'' <- switch <$> hold e' (fmap (const e) e)
143+ e'' <- liftM switch $ hold e' (fmap (const e) e)
153144 return (b, e'')
154145 , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
155146 let e' = fmap (const e) e
@@ -169,11 +160,11 @@ testCases =
169160 return (b, e'')
170161 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
171162 let e' = leftmost [e, e]
172− e'' <- switch <$> hold never (fmap (const e') e)
163+ e'' <- liftM switch $ hold never (fmap (const e') e)
173164 return (b, e'')
174165 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
175166 let e' = flip push e $ \_ -> do
176− Just <$> onceE e
167+ return . Just =<< onceE e
177168 e'' <- switchPromptly never e'
178169 return (b, e'')
179170 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -182,7 +173,7 @@ testCases =
182173 e'' <- switchPromptly never e'
183174 return (b, e'')
184175 , (,) "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
176+ let e' = flip pushAlways e $ \_ -> return $ fmap id e
186177 e'' = coincidence e'
187178 return (b, e'')
188179 , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -221,19 +212,12 @@ testCases =
221212 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222213 bb <- hold "b" e
223214 bd <- hold never . fmap (const e) =<< onceE e
224− eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
215+ eOuter <- liftM (pushAlways sample . fmap (const bb)) $ onceE e
225216 let eInner = switch bd
226217 e' = leftmost [eOuter, eInner]
227218 return (b, e')
228219 ]
229220
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−
237221test :: IO ()
238222test = do
239223 results <- forM testCases $ \(name, TestCase inputs builder) -> do
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.hs+8−15
@@ -1,11 +1,4 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE UndecidableInstances #-}
1+{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
92module Reflex.TestPlan
103 ( TestPlan(..)
114
@@ -17,11 +10,11 @@ module Reflex.TestPlan
1710
1811 ) where
1912
20−import Control.DeepSeq
21−import Control.Monad.Fix
22−import Data.Word
2313import Reflex.Class
14+import Control.Monad.Fix
2415
16+import qualified Data.IntMap as IntMap
17+import Data.IntMap
2518
2619import Prelude
2720
@@ -40,13 +33,13 @@ type TestE a = forall t m. TestPlan t m => m (Event t a)
4033type TestB a = forall t m. TestPlan t m => m (Behavior t a)
4134
4235data TestCase where
43− TestE :: (Show a, Eq a, NFData a) => TestE a -> TestCase
44− TestB :: (Show a, Eq a, NFData a) => TestB a -> TestCase
36+ TestE :: (Show a, Eq a) => TestE a -> TestCase
37+ TestB :: (Show a, Eq a) => TestB a -> TestCase
4538
4639-- Helpers to declare test cases
47−testE :: (Eq a, Show a, NFData a) => String -> TestE a -> (String, TestCase)
40+testE :: (Eq a, Show a) => String -> TestE a -> (String, TestCase)
4841testE name test = (name, TestE test)
4942
50−testB :: (Eq a, Show a, NFData a) => String -> TestB a -> (String, TestCase)
43+testB :: (Eq a, Show a) => String -> TestB a -> (String, TestCase)
5144testB name test = (name, TestB test)
5245
test/RequesterT.hsdeleted−70
@@ -1,70 +0,0 @@
1−{-# LANGUAGE FlexibleContexts, GADTs #-}
2−module Main where
3−
4−import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
7−import qualified Data.Dependent.Map as DMap
8−import Data.Dependent.Sum
9−import Data.IORef
10−import Data.List
11−import Data.Functor.Identity
12−
13−import Reflex
14−import Reflex.Host.Class
15−import Reflex.PerformEvent.Base
16−import Reflex.Requester.Base
17−import Reflex.Requester.Class
18−
19−import System.Exit (exitFailure, exitSuccess)
20−
21−data RequestInt a where
22− RequestInt :: Int -> RequestInt Int
23−
24−main :: IO ()
25−main = do
26− (Just out1, Nothing) <- runApp app1
27− print out1
28− (Just out2, Just (out3, out4)) <- runApp app2
29− print out2
30− print out3
31− print out4
32− when (sort out1 /= out1 || map (+1) out2 /= out3 || out3 /= out4) exitFailure
33−
34−runApp :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
35− => (Event t () -> RequesterT t RequestInt Identity (PerformEventT t m) (Maybe (Ref m (Maybe (EventTrigger t ())))))
36− -> IO (Maybe [Int], Maybe ([Int], [Int]))
37−runApp x = runSpiderHost $ do
38− (pulseE, pulseTriggerRef) <- newEventWithTriggerRef
39− ((mactuateRef, e), FireCommand fire) <- hostPerformEventT $ runRequesterT (x pulseE) never
40− hnd <- subscribeEvent (map unwrapRequest . DMap.toList <$> e)
41− out1 <- fireEventRefAndRead pulseTriggerRef () hnd
42− out2 <- fmap join $ forM mactuateRef $ \actuateRef -> do
43− let readPhase = do
44− mGetValue <- readEvent hnd
45− case mGetValue of
46− Nothing -> return Nothing
47− Just getValue -> fmap Just getValue
48− mactuate <- readRef actuateRef
49− mpulse <- readRef pulseTriggerRef
50− forM ((,) <$> mactuate <*> mpulse) $ \(actuate, pulse) -> do
51− _ <- fire [actuate :=> Identity ()] $ return ()
52− [Just a] <- fire [pulse :=> Identity ()] $ readPhase
53− [Just b, Nothing] <- fire [actuate :=> Identity (), pulse :=> Identity ()] $ readPhase
54− return (a,b)
55− return (out1, out2)
56−
57−unwrapRequest :: DSum tag RequestInt -> Int
58−unwrapRequest (_ :=> RequestInt i) = i
59−
60−app1 :: (Reflex t, Ref m ~ IORef, Requester t m, Response m ~ Identity, Request m ~ RequestInt) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
61−app1 e = do
62− forM_ [1..10] $ \i -> requestingIdentity (RequestInt i <$ e)
63− return Nothing
64−
65−app2 :: (Reflex t, Ref m ~ IORef, Requester t m, Response m ~ Identity, Request m ~ RequestInt, MonadAdjust t m, MonadReflexCreateTrigger t m, MonadRef m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
66−app2 e = do
67− (pulse, pulseTriggerRef) <- newEventWithTriggerRef
68− forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ e)) $ ffor pulse $ \_ -> requestingIdentity (RequestInt (i+1) <$ e)
69− return (Just pulseTriggerRef)
70−
test/hlint.hsdeleted−19
@@ -1,19 +0,0 @@
1−import Language.Haskell.HLint3 (hlint)
2−import System.Exit (exitFailure, exitSuccess)
3−
4−main :: IO ()
5−main = do
6− ideas <- hlint
7− [ "."
8− , "--ignore=Redundant do"
9− , "--ignore=Use camelCase"
10− , "--ignore=Redundant $"
11− , "--ignore=Use &&"
12− , "--ignore=Use &&&"
13− , "--ignore=Use const"
14− , "--ignore=Use >=>"
15− , "--ignore=Use ."
16− , "--ignore=Use unless"
17− , "--cpp-define=USE_TEMPLATE_HASKELL"
18− ]
19− if null ideas then exitSuccess else exitFailure
test/rootCleanup.hsdeleted−25
@@ -1,25 +0,0 @@
1−import Control.Monad
2−import Control.Concurrent
3−import Reflex
4−import Reflex.Host.Class
5−import System.Mem
6−import Data.IORef
7−import System.Exit
8−
9−main :: IO ()
10−main = do
11− numSubscriptions <- newIORef 0
12− replicateM_ 1000 $ do
13− runSpiderHost $ do
14− e <- newEventWithTrigger $ \_ -> do
15− modifyIORef' numSubscriptions succ
16− return $ modifyIORef' numSubscriptions pred
17− _ <- hold () e
18− return ()
19− replicateM_ 100 $ do
20− performMajorGC
21− threadDelay 1
22− n <- readIORef numSubscriptions
23− if n == 0
24− then putStrLn "Succeeded"
25− else putStrLn "Failed" >> exitFailure
test/semantics.hsdeleted−49
@@ -1,49 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Main (main) where
9−
10−import Reflex.Test
11−
12−import Data.Bifunctor
13−import Data.Functor
14−import Data.List
15−import qualified Reflex.Bench.Focused as Focused
16−import qualified Reflex.Test.Micro as Micro
17−
18−import System.Environment
19−import System.Exit
20−
21−import Prelude
22−
23−matchPrefixes :: [String] -> (String -> Bool)
24−matchPrefixes [] = const True
25−matchPrefixes args = \name -> any (`isPrefixOf` name) args
26−
27−
28−main :: IO ()
29−main = do
30− args <- getArgs
31−
32− case args of
33− ["--list"] -> mapM_ putStrLn (fst <$> allTests) >> exitWith (ExitFailure 1)
34− _ -> case filter (matchPrefixes args . fst) allTests of
35− [] -> putStrLn "filter did not match any tests" >> exitWith (ExitFailure 1)
36− tests -> runTests tests
37−
38− where
39− allTests = concat
40− [ makeGroup "micro" Micro.testCases
41− , makeGroup "subscribing (100,40)" (Focused.subscribing 100 40)
42− , makeGroup "firing 1000" (Focused.firing 1000)
43− , makeGroup "merge 100" (Focused.merging 100)
44− , makeGroup "fan 50" (Focused.fans 50)
45− ]
46−
47− makeGroup name tests = first (\test -> intercalate "/" [name, test]) <$> tests
48−
49−