on cbd54ea help sign in
.ghci+2
since splitsnapshots
unifiedsplit
@@ -1,2 +1,4 @@
1:set -isrc
1:set -isrc
2+:set -hide-package MonadCatchIO-mtl
3+:set -hide-package monads-fd
2:seti -XOverloadedStrings
4:seti -XOverloadedStrings
.gitignore−7
@@ -1,7 +1,4 @@
1dist
1dist
2−cabal.sandbox.config
3−.cabal-sandbox/
4−dist-*
5cabal-dev
2cabal-dev
6*.o
3*.o
7*.hi
4*.hi
@@ -32,7 +29,3 @@ tags
32hsenv.log
29hsenv.log
33\#*#
30\#*#
34.#*
31.#*
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
1+sudo: false
2−language: nix
2+before_install:
3−cache:
3+ - travis_retry cabal-$CABALVER update
4− directories:
4+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/happy/1.19.5/bin:/opt/alex/3.1.4/bin:$PATH
5− - /nix
5+install:
6−matrix:
6+ - travis_retry cabal-$CABALVER install --only-dependencies -j --enable-tests
7− include:
8− - env: COMPILER=ghc7103
9− - env: COMPILER=ghc801
10− - env: COMPILER=ghcjs
11script:
7script:
12− - set -e
8+ - cabal-$CABALVER configure -v2 --enable-tests
13− - nix-build -E "with (import <nixpkgs> {}); pkgs.haskell.packages.$COMPILER.callPackage ./default.nix { }"
9+ - cabal-$CABALVER build
14− - nix-collect-garbage -d # This is important to do right before 'script' ends so the cache gets GCed
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")
15
17
18+matrix:
19+ include:
20+ - env: CABALVER=1.18 GHCVER=7.8.4
21+ addons: {apt: {packages: [cabal-install-1.18, ghc-7.8.4, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}
22+ - env: CABALVER=1.22 GHCVER=7.10.1
23+ addons: {apt: {packages: [cabal-install-1.22, ghc-7.10.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
24+ fast_finish: true
Quickref.md+36−48
@@ -37,33 +37,28 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
37[ ] traceEventWith :: (a -> String) -> Event a -> Event a
37[ ] traceEventWith :: (a -> String) -> Event a -> Event a
38
38
39-- Transform Event to Event by sampling Behavior or Dynamic
39-- Transform Event to Event by sampling Behavior or Dynamic
40−[ ] gate :: Behavior Bool -> Event a -> Event a
40+[ ] gate :: Behavior Bool -> Event a -> Event a
41−[ ] tag :: Behavior a -> Event b -> Event a
41+[ ] tag :: Behavior a -> Event b -> Event a
42−[ ] tagPromptlyDyn :: Dynamic a -> Event b -> Event a
42+[ ] tagDyn :: Dynamic a -> Event b -> Event a
43−[ ] attach :: Behavior a -> Event b -> Event (a, b)
43+[ ] attach :: Behavior a -> Event b -> Event (a, b)
44−[ ] attachPromptlyDyn :: Dynamic 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
45+[ ] attachWith :: (a -> b -> c) -> Behavior a -> Event b -> Event c
46−[ ] attachPromptlyDynWith :: (a -> b -> c) -> Dynamic 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
47+[ ] attachWithMaybe :: (a -> b -> Maybe c) -> Behavior a -> Event b -> Event c
48−[ ] attachPromptlyDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
48+[ ] attachDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
49
49
50-- Combine multiple Events
50-- Combine multiple Events
51−[ ] <> :: Semigroup a => Event a -> Event a -> Event a
52−[ ] difference :: Event a -> Event b -> Event a
53−[ ] align :: Event a -> Event b -> Event (These a b)
51+[ ] <> :: Monoid a => Event a -> Event a -> Event a
54−[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c
55−[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
52+[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
56−[ ] leftmost :: [Event a] -> Event a
53+[ ] leftmost :: [Event a] -> Event a
57−[ ] mergeList :: [Event a] -> Event (NonEmpty a)
54+[ ] mergeList :: [Event a] -> Event (NonEmpty a)
58−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
55+[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
59−[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
56+[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
60
57
61-- Efficient one-to-many fanout
58-- Efficient one-to-many fanout
62−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
59+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
63−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
60+[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
64−[ ] select :: EventSelector k -> k a -> Event a
61+[ ] select :: EventSelector k -> k a -> Event a
65−[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
66−[ ] fanThese :: Event (These a b) -> (Event a, Event b)
67
62
68-- Event to Event via function that can sample current values
63-- Event to Event via function that can sample current values
69[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
64[ ] 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
89[H] hold :: a -> Event a -> m (Behavior a)
84[H] hold :: a -> Event a -> m (Behavior a)
90
85
91-- Transform Behavior to Behavior using function
86-- Transform Behavior to Behavior using function
92−[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
87+[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
93−[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
88+[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
94−[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
89+[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
95−[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
90+[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
96−[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
91+[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
97-- ... plus many more due to typeclass membership
92-- ... plus many more due to typeclass membership
98
93
99-- Behavior to Behavior by sampling current values
94-- Behavior to Behavior by sampling current values
@@ -122,23 +117,23 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
122[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
117[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
123
118
124-- Transform Dynamic to Dynamic using function
119-- Transform Dynamic to Dynamic using function
125−[ ] ffor :: Dynamic a -> (a -> b) -> Dynamic b
120+[H] forDyn :: Dynamic a -> (a -> b) -> m (Dynamic b)
126−[ ] fmap :: (a -> b) -> Dynamic a -> 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
127−[ ] splitDynPure :: Dynamic (a, b) -> (Dynamic a, Dynamic b)
124+[H] splitDyn :: Dynamic (a, b) -> m (Dynamic a, Dynamic b)
128
125
129-- Combine multiple Dynamics
126-- Combine multiple Dynamics
130−[ ] mconcat :: Monoid a => [Dynamic a] -> Dynamic a
127+[H] mconcatDyn :: Monoid a => [Dynamic a] -> m (Dynamic a)
131−[ ] distributeDMapOverDynPure :: GCompare k => DMap (WrapArg Dynamic k) -> Dynamic (DMap k)
128+[H] distributeDMapOverDyn :: GCompare k => DMap (WrapArg Dynamic k) -> m (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
129+[H] combineDyn :: (a -> b -> c) -> Dynamic a -> Dynamic b -> m (Dynamic c)
135
130
136-- Efficient one-to-many fanout
131-- Efficient one-to-many fanout
137−[ ] demux :: Ord k => Dynamic k -> Demux k
132+[ ] demux :: Ord k => Dynamic k -> Demux k
138−[ ] demuxed :: Eq k => Demux k -> k -> Dynamic Bool
133+[H] getDemuxed :: Eq k => Demux k -> k -> m (Dynamic Bool)
139
134
140-- Dynamic to Dynamic, removing updates w/o value change
135-- 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
142
137
143-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
138-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
144[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
139[ ] 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
161-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
156-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
162[ ] coincidence :: Event (Event a) -> Event a
157[ ] coincidence :: Event (Event a) -> Event a
163
158
164−-- Flatten Behavior-of-Behavior to Behavior. Behavior is an instance of Monad,
165−-- so the 'join' function from 'Control.Monad' suffices. The output of 'join' is
166−-- a Behavior whose value is equal to the current value of the current value of
167−-- the input.
168−[ ] join :: Behavior (Behavior a) -> Behavior a
169−
170−
171-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
159-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
172−-- Output updated whenever inner OR outer Dynamic updates.
160+-- Output updated whenever inner OR outer Dynamic updates.
173−[ ] join :: Dynamic (Dynamic a) -> Dynamic a
161+[ ] joinDyn :: Dynamic (Dynamic a) -> Dynamic a
174[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
162[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
175
163
176-- Analogous to 'hold': Create a Behavior that is initially identical to the
164-- 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 #-}
2{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE BangPatterns #-}
4{-# LANGUAGE TupleSections #-}
3{-# LANGUAGE TupleSections #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
6−
7−-- The instance for NFData (TVar a) is an orphan, but necessary here
8−{-# OPTIONS_GHC -fno-warn-orphans #-}
9−
10module Main where
6module Main where
11
7
12−import Control.Concurrent.STM
8+import Data.Functor.Misc
13−import Control.DeepSeq
14−import Control.Exception (evaluate)
15−import Control.Monad.Identity
9+import Control.Monad.Primitive
16import Control.Monad.IO.Class
10import Control.Monad.IO.Class
17−import Criterion.Main
11+import Control.Monad.Identity
18−import Data.Dependent.Map (DMap)
19−import qualified Data.Dependent.Map as DMap
20import Data.Dependent.Sum
12import Data.Dependent.Sum
21−import Data.Functor.Misc
13+import Control.Concurrent.STM
14+import Control.Applicative
15+import System.IO.Unsafe
22import Data.IORef
16import Data.IORef
17+import Control.DeepSeq
18+import Control.Exception (evaluate)
19+import Control.Monad
23import Reflex
20import Reflex
24import Reflex.Host.Class
21import 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+
25
28
26main :: IO ()
29main :: IO ()
27main = defaultMain
30main = defaultMain
@@ -39,14 +42,15 @@ newtype WHNF a = WHNF a
39instance NFData (WHNF a) where
42instance NFData (WHNF a) where
40 rnf (WHNF a) = seq a ()
43 rnf (WHNF a) = seq a ()
41
44
42−withSetup :: NFData b => String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
45+withSetup :: NFData b => String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
43withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
46withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
44 bench name . nfIO $ runSpiderHost (action a)
47 bench name . nfIO $ runSpiderHost (action a)
45
48
46−withSetupWHNF :: String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
49+withSetupWHNF :: String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
47withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
50withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
48 bench name . whnfIO $ runSpiderHost (action a)
51 bench name . whnfIO $ runSpiderHost (action a)
49
52
53+
50micros :: [Benchmark]
54micros :: [Benchmark]
51micros =
55micros =
52 [ bench "newIORef" $ whnfIO $ void $ newIORef ()
56 [ bench "newIORef" $ whnfIO $ void $ newIORef ()
@@ -69,41 +73,40 @@ micros =
69 (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
73 (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
70 , withSetupWHNF "fireEventsOnly"
74 , withSetupWHNF "fireEventsOnly"
71 (newEventWithTriggerRef >>= subscribePair)
75 (newEventWithTriggerRef >>= subscribePair)
72− (\(_, trigger) -> do
76+ (\(subd, trigger) -> do
73 Just key <- liftIO $ readIORef trigger
77 Just key <- liftIO $ readIORef trigger
74 fireEvents [key :=> Identity (42 :: Int)])
78 fireEvents [key :=> Identity (42 :: Int)])
75 , withSetupWHNF "fireEventsAndRead(head/merge1)"
79 , withSetupWHNF "fireEventsAndRead(head/merge1)"
76 (setupMerge 1 >>= subscribePair)
80 (setupMerge 1 >>= subscribePair)
77− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
81+ (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
78 , withSetupWHNF "fireEventsAndRead(head/merge100)"
82 , withSetupWHNF "fireEventsAndRead(head/merge100)"
79 (setupMerge 100 >>= subscribePair)
83 (setupMerge 100 >>= subscribePair)
80− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
84+ (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
81 , withSetupWHNF "fireEventsAndRead(head/merge10000)"
85 , withSetupWHNF "fireEventsAndRead(head/merge10000)"
82 (setupMerge 10000 >>= subscribePair)
86 (setupMerge 10000 >>= subscribePair)
83− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
87+ (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
84 , withSetupWHNF "fireEventsOnly(head/merge100)"
88 , withSetupWHNF "fireEventsOnly(head/merge100)"
85 (setupMerge 100 >>= subscribePair)
89 (setupMerge 100 >>= subscribePair)
86− (\(_, t:_) -> do
90+ (\(subd, t:riggers) -> do
87 Just key <- liftIO $ readIORef t
91 Just key <- liftIO $ readIORef t
88 fireEvents [key :=> Identity (42 :: Int)])
92 fireEvents [key :=> Identity (42 :: Int)])
89− , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev, _) -> hold (42 :: Int) ev
93+ , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev,trigger) -> hold (42 :: Int) ev
90− , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
94+ , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
91 ]
95 ]
92
96
93setupMerge :: Int
97setupMerge :: Int
94− -> SpiderHost Global ( Event (SpiderEnv Global) (DMap (Const2 Int a) Identity)
98+ -> SpiderHost (Event Spider (DM.DMap (Const2 Int a) Identity),
95− , [IORef (Maybe (EventTrigger Spider a))]
99+ [IORef (Maybe (EventTrigger Spider a))])
96− )
97setupMerge num = do
100setupMerge num = do
98− (evs, triggers) <- unzip <$> replicateM num newEventWithTriggerRef
101+ (evs, triggers) <- unzip <$> replicateM 100 newEventWithTriggerRef
99− let !m = DMap.fromList [Const2 i :=> v | (i,v) <- zip [0..] evs]
102+ let !m = DM.fromList [(Const2 i) :=> v | (i,v) <- zip [0..] evs]
100 pure (merge m, triggers)
103 pure (merge m, triggers)
101
104
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)
103subscribePair (ev, b) = (,b) <$> subscribeEvent ev
106subscribePair (ev, b) = (,b) <$> subscribeEvent ev
104
107
105−fireAndRead :: IORef (Maybe (EventTrigger (SpiderEnv Global) a)) -> a -> EventHandle (SpiderEnv Global) b
108+fireAndRead :: IORef (Maybe (EventTrigger Spider a)) -> a -> EventHandle Spider b
106− -> SpiderHost Global (Maybe b)
109+ -> SpiderHost (Maybe b)
107fireAndRead trigger val subd = do
110fireAndRead trigger val subd = do
108 Just key <- liftIO $ readIORef trigger
111 Just key <- liftIO $ readIORef trigger
109 fireEventsAndRead [key :=> Identity val] $ readEvent subd >>= sequence
112 fireEventsAndRead [key :=> Identity val] $ readEvent subd >>= sequence
bench/RunAll.hs+42−113
@@ -1,18 +1,4 @@
1−{-# LANGUAGE BangPatterns #-}
1+{-# LANGUAGE ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE ForeignFunctionInterface #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PatternSynonyms #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TemplateHaskell #-}
13−{-# LANGUAGE TupleSections #-}
14−{-# LANGUAGE TypeSynonymInstances #-}
15−{-# LANGUAGE ViewPatterns #-}
16{-# OPTIONS_GHC -fno-warn-orphans #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
17
3
18module Main where
4module Main where
@@ -23,135 +9,78 @@ import Criterion.Types
23import Reflex
9import Reflex
24import Reflex.Host.Class
10import Reflex.Host.Class
25
11
26−import Reflex.Plan.Reflex
27import Reflex.TestPlan
12import Reflex.TestPlan
13+import Reflex.Plan.Reflex
28
14
29−import qualified Reflex.Bench.Focused as Focused
30import Reflex.Spider.Internal (SpiderEventHandle)
15import Reflex.Spider.Internal (SpiderEventHandle)
16+import qualified Reflex.Bench.Focused as Focused
31
17
32import Control.Applicative
18import Control.Applicative
33import Control.DeepSeq (NFData (..))
19import Control.DeepSeq (NFData (..))
34
20
35−import Prelude
36import System.IO
21import System.IO
37import System.Mem
22import System.Mem
38−
23+import Prelude
39−import Control.Arrow
40−import Control.Concurrent
41−import Control.Concurrent.STM
42−import Control.Exception
43−import Control.Monad
44−import Control.Monad.Trans
45−import Data.Bool
46−import Data.Function
47−import Data.Int
48−import Data.IORef
49−import Data.Monoid
50−import Data.Time.Clock
51−import Debug.Trace.LocationTH
52−import GHC.Stats
53−import System.Environment
54−import System.Mem.Weak
55−import System.Process
56−import Text.Read
57−
58−import Unsafe.Coerce
59−
60−import Data.Map (Map)
61−import qualified Data.Map as Map
62
24
63type MonadReflexHost' t m = (MonadReflexHost t m, MonadIORef m, MonadIORef (HostFrame t))
25type MonadReflexHost' t m = (MonadReflexHost t m, MonadIORef m, MonadIORef (HostFrame t))
64
26
65
27
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)
67setupFiring p = do
29setupFiring p = do
68 (e, s) <- runPlan p
30 (e, s) <- runPlan p
69 h <- subscribeEvent e
31 h <- subscribeEvent e
70− return (h, s)
32+ return (Ignore h, s)
71
33
72-- Hack to avoid the NFData constraint for EventHandle which is a synonym
34-- Hack to avoid the NFData constraint for EventHandle which is a synonym
73newtype Ignore a = Ignore a
35newtype Ignore a = Ignore a
74instance NFData (Ignore a) where
36instance NFData (Ignore a) where
75 rnf !_ = ()
37 rnf !_ = ()
76
38
77−instance NFData (SpiderEventHandle x a) where
39+instance NFData (SpiderEventHandle a) where
78 rnf !_ = ()
40 rnf !_ = ()
79
41
80instance NFData (Behavior t a) where
42instance NFData (Behavior t a) where
81 rnf !_ = ()
43 rnf !_ = ()
82
44
83instance NFData (Firing t) where
45instance NFData (Firing t) where
84− rnf !_ = ()
46+ rnf !(Firing _ _) = ()
85
47
86-- Measure the running time
48-- Measure the running time
87−benchFiring :: forall t m. (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> TestCase -> Int -> IO ()
49+benchFiring :: (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> (String, TestCase) -> Benchmark
88−benchFiring runHost tc n = runHost $ do
50+benchFiring runHost (name, TestE p) = env setup (\e -> bench name $ whnfIO $ run e) where
89− let runIterations :: m a -> m ()
51+ run (Ignore h, s) = runHost (readSchedule s (readEvent' h)) >> performGC
90− runIterations test = replicateM_ (10*n) $ do
52+ setup = runHost $ setupFiring p
91− result <- test
53+
92− liftIO $ evaluate result
54+benchFiring runHost (name, TestB p) = env setup (\e -> bench name $ whnfIO $ run e) where
93− case tc of
55+ run (b, s) = runHost (readSchedule s (sample b)) >> performGC
94− TestE p -> do
56+ setup = runHost $ do
95− (h, s) <- setupFiring p
96− runIterations $ readSchedule_ s $ readEvent' h
97− TestB p -> do
98 (b, s) <- runPlan p
57 (b, s) <- runPlan p
99− runIterations $ readSchedule_ (makeDense s) $ sample b
58+ return (b, makeDense s)
100−
101−waitForFinalizers :: IO ()
102−waitForFinalizers = do
103− performGC
104− x <- getCurrentTime
105− isFinalized <- newIORef False
106− mkWeakPtr x $ Just $ writeIORef isFinalized True
107− performGC
108− fix $ \loop -> do
109− f <- readIORef isFinalized
110− unless f $ do
111− threadDelay 1
112− loop
113−
114−benchmarks :: [(String, Int -> IO ())]
115−benchmarks = implGroup "spider" runSpiderHost cases
116− where
117− implGroup :: (MonadReflexHost' t m, MonadSample t m) => String -> (forall a. m a -> IO a) -> [(String, TestCase)] -> [(String, Int -> IO ())]
118− implGroup name runHost = group name . fmap (second (benchFiring runHost))
119− group name = fmap $ first ((name <> "/") <>)
120− sub n frames = group ("subscribing " ++ show (n, frames)) $ Focused.subscribing n frames
121− firing n = group ("firing " <> show n) $ Focused.firing n
122− merging n = group ("merging " <> show n) $ Focused.merging n
123− dynamics n = group ("dynamics " <> show n) $ Focused.dynamics n
124− cases = concat
125− [ sub 100 40
126− , dynamics 100
127− , dynamics 1000
128− , firing 1000
129− , firing 10000
130− , merging 10
131− , merging 50
132− , merging 100
133− , merging 200
134− ]
135−
136−pattern RunTestCaseFlag = "--run-test-case"
137−
138−spawnBenchmark :: String -> Benchmark
139−spawnBenchmark name = Benchmark name $ Benchmarkable $ \n -> do
140− self <- getExecutablePath
141− callProcess self [RunTestCaseFlag, name, show n, "+RTS", "-N1"]
142−
143−foreign import ccall unsafe "myCapabilityHasOtherRunnableThreads" myCapabilityHasOtherRunnableThreads :: IO Bool
144
59
145main :: IO ()
60main :: IO ()
146main = do
61main = do
147− args <- getArgs
62+ hSetBuffering stdout LineBuffering
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
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
1+{ mkDerivation, dependent-map, dependent-sum
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
2+, mtl, ref-tf, semigroups, these, MemoTrie, exception-transformers
4−, monad-control, mtl, primitive, ref-tf, semigroupoids
3+, haskell-src-exts, haskell-src-meta
5−, semigroups, split, stdenv, stm, syb, template-haskell
6−, these, transformers, transformers-compat, prim-uniq
7−, data-default
8−, useTemplateHaskell ? true
9}:
4}:
10mkDerivation {
5mkDerivation {
11 pname = "reflex";
6 pname = "reflex";
12− version = "0.5.0";
7+ version = "0.4.0";
13 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
8 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
14− libraryHaskellDepends = [
9+ buildDepends = [
15− base bifunctors containers dependent-map dependent-sum
10+ dependent-map dependent-sum mtl ref-tf semigroups these exception-transformers
16− exception-transformers lens
17− MemoTrie monad-control mtl primitive ref-tf semigroupoids
18− semigroups stm syb template-haskell these transformers
19− transformers-compat prim-uniq
20− base bifunctors containers deepseq dependent-map dependent-sum
21− mtl ref-tf split transformers data-default
22− ] ++ (if !useTemplateHaskell then [] else [
23 haskell-src-exts haskell-src-meta
11 haskell-src-exts haskell-src-meta
24− ]);
25− testHaskellDepends = if ghc.isGhcjs or false then [] else [
26− hlint
27 ];
12 ];
28− configureFlags = if useTemplateHaskell then [] else [
13+ testDepends = [
29− "-f-use-template-haskell"
14+ MemoTrie
30 ];
15 ];
31− homepage = "https://github.com/reflex-frp/reflex";
16+ license = null;
32− description = "Higher-order Functional Reactive Programming";
33− license = stdenv.lib.licenses.bsd3;
34}
17}
reflex.cabal+42−175
@@ -1,5 +1,5 @@
1Name: reflex
1Name: reflex
2−Version: 0.5.0
2+Version: 0.4.0
3Synopsis: Higher-order Functional Reactive Programming
3Synopsis: Higher-order Functional Reactive Programming
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5License: BSD3
5License: BSD3
@@ -13,224 +13,91 @@ Cabal-version: >=1.9.2
13homepage: https://github.com/reflex-frp/reflex
13homepage: https://github.com/reflex-frp/reflex
14bug-reports: https://github.com/reflex-frp/reflex/issues
14bug-reports: https://github.com/reflex-frp/reflex/issues
15
15
16−flag specialize-to-spidertimeline-global
17− description: Specialize all Reflex functions to the SpiderTimeline Global implementation. This may improve performance, but will disable all other implementations.
18− default: False
19− manual: True
20−
21−flag use-reflex-optimizer
22− description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
23− default: False
24− manual: True
25−
26−flag use-template-haskell
27− description: Use template haskell to generate lenses
28− default: True
29− manual: True
30−
31−flag expose-all-unfoldings
32− description: Build the library with -fexpose-all-unfoldings which can help client code specialize better
33− default: False
34− manual: True
35−
36−flag debug-trace-events
37− description: add instrumentation that outputs the stack trace of the definition of an event whenever it is subscribed to. Warning: It is very slow!
38− default: False
39− manual: True
40−
41library
16library
42 hs-source-dirs: src
17 hs-source-dirs: src
43 build-depends:
18 build-depends:
44− MemoTrie == 0.6.*,
45− base >= 4.7 && < 4.11,
19+ base >= 4.7 && < 4.10,
46− bifunctors >= 5.2 && < 5.5,
20+ dependent-sum >= 0.3 && < 0.5,
47− containers == 0.5.*,
48− data-default >= 0.5 && < 0.8,
49− dependent-map >= 0.2.4 && < 0.3,
21+ dependent-map == 0.2.*,
50− exception-transformers == 0.4.*,
22+ semigroups >= 0.16 && < 0.19,
51− lens >= 4.7 && < 5,
52− monad-control >= 1.0.1 && < 1.1,
53 mtl >= 2.1 && < 2.3,
23 mtl >= 2.1 && < 2.3,
54− prim-uniq >= 0.1.0.1 && < 0.2,
24+ containers == 0.5.*,
25+ these >= 0.4 && < 0.8,
55 primitive >= 0.5 && < 0.7,
26 primitive >= 0.5 && < 0.7,
27+ template-haskell >= 2.9 && < 2.12,
56 ref-tf == 0.4.*,
28 ref-tf == 0.4.*,
57− reflection == 2.1.*,
29+ exception-transformers == 0.4.*,
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,
63 transformers >= 0.2,
30 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
65
35
66 exposed-modules:
36 exposed-modules:
67− Data.Functor.Misc,
68− Data.WeakBag,
69 Reflex,
37 Reflex,
38+ Reflex.Spider,
39+ Reflex.Spider.Internal,
70 Reflex.Class,
40 Reflex.Class,
71− Reflex.EventWriter,
72 Reflex.Dynamic,
41 Reflex.Dynamic,
73− Reflex.Dynamic.Uniq,
42+ Reflex.Dynamic.TH,
74− Reflex.DynamicWriter,
75− Reflex.FunctorMaybe,
76 Reflex.Host.Class,
43 Reflex.Host.Class,
77− Reflex.Patch,
44+ Data.Functor.Misc
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
88
45
46+ other-extensions: TemplateHaskell
89 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
47 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
105
48
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
124 type: exitcode-stdio-1.0
50 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
126 hs-source-dirs: test
55 hs-source-dirs: test
127− ghc-options: -O2 -Wall -rtsopts
128 build-depends:
56 build-depends:
129 base,
57 base,
130− bifunctors,
58+ reflex,
59+ ref-tf,
60+ mtl,
131 containers,
61 containers,
132− deepseq >= 1.3 && < 1.5,
133 dependent-map,
62 dependent-map,
134− dependent-sum,
63+ MemoTrie == 0.6.*
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
192
64
193benchmark spider-bench
65benchmark spider-bench
194 type: exitcode-stdio-1.0
66 type: exitcode-stdio-1.0
195 hs-source-dirs: bench
67 hs-source-dirs: bench
196 main-is: Main.hs
68 main-is: Main.hs
197− ghc-options: -Wall -O2 -rtsopts
69+ ghc-options: -O2 -rtsopts
198 build-depends:
70 build-depends:
199 base,
71 base,
200− criterion == 1.1.*,
201− deepseq >= 1.3 && < 1.5,
202− dependent-map,
203 dependent-sum,
72 dependent-sum,
73+ dependent-map,
74+ transformers >= 0.3 && < 0.6,
75+ stm == 2.4.*,
76+ deepseq >= 1.3 && < 1.5,
204 mtl,
77 mtl,
205 primitive,
78 primitive,
206− reflex,
79+ criterion >= 1.1 && < 1.3,
207− stm,
80+ reflex
208− transformers >= 0.3
209
81
210benchmark saulzar-bench
82benchmark saulzar-bench
211 type: exitcode-stdio-1.0
83 type: exitcode-stdio-1.0
212 hs-source-dirs: bench test
84 hs-source-dirs: bench test
213− c-sources: bench-cbits/checkCapability.c
214 main-is: RunAll.hs
85 main-is: RunAll.hs
215− ghc-options: -Wall -O2 -rtsopts -threaded
86+ ghc-options: -O2 -rtsopts
216− ghc-prof-options: -fprof-auto
217 build-depends:
87 build-depends:
218 base,
88 base,
219 containers == 0.5.*,
89 containers == 0.5.*,
220− criterion == 1.1.*,
90+ ref-tf == 0.4.*,
221− deepseq >= 1.3 && < 1.5,
222− dependent-map,
223 dependent-sum,
91 dependent-sum,
224− loch-th,
92+ dependent-map,
93+ transformers >= 0.3 && < 0.6,
94+ stm == 2.4.*,
95+ deepseq >= 1.3 && < 1.5,
225 mtl,
96 mtl,
226 primitive,
97 primitive,
227− process,
98+ criterion >= 1.1 && < 1.3,
228− ref-tf,
229− reflex,
230 split,
99 split,
231− stm,
100+ reflex
232− time,
233− transformers >= 0.3
234
101
235source-repository head
102source-repository head
236 type: git
103 type: git
src/Data/Functor/Misc.hs+41−202
@@ -1,78 +1,30 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE KindSignatures, GADTs, DeriveDataTypeable, RankNTypes, ScopedTypeVariables, PolyKinds #-}
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
2+module Data.Functor.Misc where
20− ( -- * Const2
3+
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
50import Data.GADT.Compare
4import Data.GADT.Compare
51−import Data.GADT.Show
52import Data.Map (Map)
5import Data.Map (Map)
53import qualified Data.Map as Map
6import qualified Data.Map as Map
54−import Data.Some (Some)
7+import Data.Dependent.Map (DMap, DSum (..))
55−import qualified Data.Some as Some
8+import qualified Data.Dependent.Map as DMap
56−import Data.These
57import Data.Typeable hiding (Refl)
9import Data.Typeable hiding (Refl)
10+import Data.These
11+import Control.Monad.Identity
58
12
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
13+data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
66− Const2 :: k -> Const2 k v v
14+ WrapArg :: f a -> WrapArg g f (g a)
67− deriving (Typeable)
68
15
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
70
18
71−instance Show k => GShow (Const2 k v) where
19+instance GCompare f => GCompare (WrapArg g f) where
72− gshowsPrec n x@(Const2 _) = showsPrec n x
20+ gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
21+ GLT -> GLT
22+ GEQ -> GEQ
23+ GGT -> GGT
73
24
74−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
25+data Const2 :: * -> * -> * -> * where
75− showTaggedPrec (Const2 _) = showsPrec
26+ Const2 :: k -> Const2 k v v
27+ deriving (Typeable)
76
28
77instance Eq k => GEq (Const2 k v) where
29instance Eq k => GEq (Const2 k v) where
78 geq (Const2 a) (Const2 b) =
30 geq (Const2 a) (Const2 b) =
@@ -86,63 +38,13 @@ instance Ord k => GCompare (Const2 k v) where
86 EQ -> GEQ
38 EQ -> GEQ
87 GT -> GGT
39 GT -> GGT
88
40
89−-- | Convert a 'DMap' to a regular 'Map'
41+{-# INLINE sequenceDmap #-}
90−dmapToMap :: DMap (Const2 k v) Identity -> Map k v
42+sequenceDmap :: (Monad m, GCompare f) => DMap f m -> m (DMap f Identity)
91−dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
43+sequenceDmap = DMap.foldrWithKey (\k mv mx -> mx >>= \x -> mv >>= \v -> return $ DMap.insert k (Identity v) x)
92−
44+ (return DMap.empty)
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
136
45
137{-# INLINE combineDMapsWithKey #-}
46{-# 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.
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
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
146combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
48combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
147 where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
49 where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
148 go [] hs = map (\(hk :=> hv) -> hk :=> f hk (That hv)) hs
50 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
152 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
54 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
153 GGT -> (hk :=> f hk (That hv)) : go gs hs'
55 GGT -> (hk :=> f hk (That hv)) : go gs hs'
154
56
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
215wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
57wrapDMap :: (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
217
59
218−{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
219−-- | Replace one functor for a 'DMap''s values with a different functor
220rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
60rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
221−rewrapDMap = DMap.map
61+rewrapDMap f = DMap.fromDistinctAscList . map (\(k :=> v) -> k :=> f v) . DMap.toAscList
222−
62+
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
63+unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
226−unwrapDMap f = DMap.map $ Identity . f
64+unwrapDMap f = DMap.fromDistinctAscList . map (\(k :=> v) -> k :=> Identity (f v)) . DMap.toAscList
227−
65+
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
66+unwrapDMapMaybe :: (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
231−unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
67+unwrapDMapMaybe f m = DMap.fromDistinctAscList [k :=> Identity w | (k :=> v) <- DMap.toAscList m, Just w <- [f v]]
232−
68+
233−{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
69+mapToDMap :: Map k v -> DMap (Const2 k v) Identity
234−-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
70+mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
235−-- underlying functor
71+
236−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
72+mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
237−extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
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 #-}
1+module Reflex ( module Reflex.Class
2−-- | This module exports all of the commonly-used functionality of Reflex; if
2+ , module Reflex.Dynamic
3−-- you are just getting started with Reflex, this is probably what you want.
3+ , module Reflex.Dynamic.TH
4−module Reflex
4+ , module Reflex.Spider
5− ( module X
6− ) where
5+ ) where
7
6
8−import Reflex.Class as X
7+import Reflex.Class
9−import Reflex.EventWriter as X
8+import Reflex.Dynamic
10−import Reflex.Dynamic as X
11−#ifdef USE_TEMPLATE_HASKELL
12−import Reflex.Dynamic.TH as X
9+import Reflex.Dynamic.TH
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
10+import Reflex.Spider
23−import Reflex.TriggerEvent.Base as X
24−import Reflex.TriggerEvent.Class as X
src/Reflex/Class.hs+189−836
@@ -1,435 +1,67 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, 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
2+module Reflex.Class where
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
147
3
148import Control.Applicative
4import Control.Applicative
149−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
5+import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, 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_)
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_)
152−import Control.Monad.Trans.Cont (ContT ())
8+import Control.Monad.Trans.Writer (WriterT())
153−import Control.Monad.Trans.Except (ExceptT ())
9+import Control.Monad.Trans.Except (ExceptT())
10+import Control.Monad.Trans.Cont (ContT())
154−import Control.Monad.Trans.RWS (RWST ())
11+import Control.Monad.Trans.RWS (RWST())
155−import Control.Monad.Trans.Writer (WriterT ())
12+import Data.List.NonEmpty (NonEmpty (..))
13+import Data.These
156import Data.Align
14import Data.Align
157−import Data.Bifunctor
15+import Data.GADT.Compare (GEq (..), (:~:) (..))
158−import Data.Coerce
16+import Data.GADT.Show (GShow (..))
159−import Data.Default
17+import Data.Dependent.Sum (ShowTag (..))
18+import Data.Map (Map)
160−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
19+import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
161import qualified Data.Dependent.Map as DMap
20import 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
167import Data.Functor.Misc
21import Data.Functor.Misc
168−import Data.Functor.Plus
22+import Data.Semigroup
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
176import Data.Traversable
23import 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
191
24
192-- Note: must come last to silence warnings due to AMP on GHC < 7.10
25-- 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)
194
27
195import Debug.Trace (trace)
28import Debug.Trace (trace)
196
29
197−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
30+class (MonadHold t (PushM t), MonadSample t (PullM t), MonadFix (PushM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
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
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
218 data Behavior t :: * -> *
32 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
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")
221− -- the given type (its "occurrence type")
222 data Event t :: * -> *
34 data Event t :: * -> *
223− -- | A container for a value that can change over time and allows
35+ -- | An Event with no occurrences
224− -- notifications on changes. Basically a combination of a 'Behavior' and an
36+ never :: Event t a
225− -- 'Event', with a rule that the 'Behavior' will change if and only if the
37+ -- | Create a Behavior that always has the given value
226− -- 'Event' fires.
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
227− data Dynamic t :: * -> *
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
228− -- | An 'Incremental' is a more general form of a 'Dynamic'.
40+ push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
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 :: * -> *
233 -- | A monad for doing complex push-based calculations efficiently
41 -- | A monad for doing complex push-based calculations efficiently
234 type PushM t :: * -> *
42 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
235 -- | A monad for doing complex pull-based calculations efficiently
45 -- | A monad for doing complex pull-based calculations efficiently
236 type PullM t :: * -> *
46 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
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
253− -- that are occurring simultaneously
254 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
48 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
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
256− -- be partially applied, and then the result applied repeatedly to create
257− -- child events
258 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
50 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
51+ -- | Create an Event that will occur whenever the currently-selected input Event occurs
260− -- 'Event' occurs
261 switch :: Behavior t (Event t a) -> Event t a
52 switch :: Behavior t (Event t a) -> Event t a
262− -- | Create an 'Event' that will occur whenever the input event is occurring
53+ -- | Create an Event that will occur whenever the input event is occurring and its occurrence value, another Event, is also occurring
263− -- and its occurrence value, another 'Event', is also occurring
264 coincidence :: Event t (Event t a) -> Event t a
54 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
376
55
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'.
398class (Applicative m, Monad m) => MonadSample t m | m -> t where
56class (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
400 sample :: Behavior t a -> m a
58 sample :: Behavior t a -> m a
401
59
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.
405class MonadSample t m => MonadHold t m where
60class MonadSample t m => MonadHold t m where
406− -- | Create a new 'Behavior' whose value will initially be equal to the given
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.
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.
412 hold :: a -> Event t a -> m (Behavior t a)
62 hold :: a -> Event t a -> m (Behavior t a)
413− -- | Create a 'Dynamic' value using the given initial value that changes every
63+
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
64+newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
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− }
433
65
434--------------------------------------------------------------------------------
66--------------------------------------------------------------------------------
435-- Instances
67-- Instances
@@ -440,114 +72,85 @@ instance MonadSample t m => MonadSample t (ReaderT r m) where
440
72
441instance MonadHold t m => MonadHold t (ReaderT r m) where
73instance MonadHold t m => MonadHold t (ReaderT r m) where
442 hold a0 = lift . hold a0
74 hold a0 = lift . hold a0
443− holdDyn a0 = lift . holdDyn a0
444− holdIncremental a0 = lift . holdIncremental a0
445
75
446instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
76instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
447 sample = lift . sample
77 sample = lift . sample
448
78
449instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
79instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
450 hold a0 = lift . hold a0
80 hold a0 = lift . hold a0
451− holdDyn a0 = lift . holdDyn a0
452− holdIncremental a0 = lift . holdIncremental a0
453
81
454instance MonadSample t m => MonadSample t (StateT s m) where
82instance MonadSample t m => MonadSample t (StateT s m) where
455 sample = lift . sample
83 sample = lift . sample
456
84
457instance MonadHold t m => MonadHold t (StateT s m) where
85instance MonadHold t m => MonadHold t (StateT s m) where
458 hold a0 = lift . hold a0
86 hold a0 = lift . hold a0
459− holdDyn a0 = lift . holdDyn a0
460− holdIncremental a0 = lift . holdIncremental a0
461
87
462instance MonadSample t m => MonadSample t (ExceptT e m) where
88instance MonadSample t m => MonadSample t (ExceptT e m) where
463 sample = lift . sample
89 sample = lift . sample
464
90
465instance MonadHold t m => MonadHold t (ExceptT e m) where
91instance MonadHold t m => MonadHold t (ExceptT e m) where
466 hold a0 = lift . hold a0
92 hold a0 = lift . hold a0
467− holdDyn a0 = lift . holdDyn a0
468− holdIncremental a0 = lift . holdIncremental a0
469
93
470instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
94instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
471 sample = lift . sample
95 sample = lift . sample
472
96
473instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
97instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
474 hold a0 = lift . hold a0
98 hold a0 = lift . hold a0
475− holdDyn a0 = lift . holdDyn a0
476− holdIncremental a0 = lift . holdIncremental a0
477
99
478instance MonadSample t m => MonadSample t (ContT r m) where
100instance MonadSample t m => MonadSample t (ContT r m) where
479 sample = lift . sample
101 sample = lift . sample
480
102
481instance MonadHold t m => MonadHold t (ContT r m) where
103instance MonadHold t m => MonadHold t (ContT r m) where
482 hold a0 = lift . hold a0
104 hold a0 = lift . hold a0
483− holdDyn a0 = lift . holdDyn a0
484− holdIncremental a0 = lift . holdIncremental a0
485
105
486--------------------------------------------------------------------------------
106--------------------------------------------------------------------------------
487-- Convenience functions
107-- Convenience functions
488--------------------------------------------------------------------------------
108--------------------------------------------------------------------------------
489
109
490−-- | Create an 'Event' from another 'Event'. The provided function can sample
110+-- | Create an Event from another Event.
491−-- 'Behavior's and hold 'Event's.
111+-- The provided function can sample 'Behavior's and hold 'Event's.
492pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
112pushAlways :: 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)
494
114
495-- | Flipped version of 'fmap'.
115-- | Flipped version of 'fmap'.
496ffor :: Functor f => f a -> (a -> b) -> f b
116ffor :: Functor f => f a -> (a -> b) -> f b
497ffor = flip fmap
117ffor = flip fmap
498
118
119+instance Reflex t => Functor (Behavior t) where
120+ fmap f = pull . liftM f . sample
121+
499instance Reflex t => Applicative (Behavior t) where
122instance Reflex t => Applicative (Behavior t) where
500 pure = constant
123 pure = constant
501 f <*> x = pull $ sample f `ap` sample x
124 f <*> x = pull $ sample f `ap` sample x
502 _ *> b = b
125 _ *> b = b
503 a <* _ = a
126 a <* _ = a
504
127
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−
522instance Reflex t => Monad (Behavior t) where
128instance Reflex t => Monad (Behavior t) where
523 a >>= f = pull $ sample a >>= sample . f
129 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.
525 return = constant
131 return = constant
526 fail = error "Monad (Behavior t) does not support fail"
132 fail = error "Monad (Behavior t) does not support fail"
527
133
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−
542instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
134instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
543 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
135 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
544− sconcat = pull . fmap sconcat . mapM sample
136+ sconcat = pull . liftM sconcat . mapM sample
545#if MIN_VERSION_semigroups(0,17,0)
137#if MIN_VERSION_semigroups(0,17,0)
546 stimes n = fmap $ stimes n
138 stimes n = fmap $ stimes n
547#else
139#else
548 times1p n = fmap $ times1p n
140 times1p n = fmap $ times1p n
549#endif
141#endif
550
142
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+
551-- | Flipped version of 'fmapMaybe'.
154-- | Flipped version of 'fmapMaybe'.
552fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
155fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
553fforMaybe = flip fmapMaybe
156fforMaybe = flip fmapMaybe
@@ -557,465 +160,215 @@ fforMaybe = flip fmapMaybe
557ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
160ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
558ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
161ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
559
162
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
163+instance Reflex t => FunctorMaybe (Event t) where
567− evf <.> evx = coincidence (fmap (<$> evx) evf)
164+ fmapMaybe f = push $ return . f
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
573
165
574instance Reflex t => Functor (Event t) where
166instance Reflex t => Functor (Event t) where
575− {-# INLINE fmap #-}
576 fmap f = fmapMaybe $ Just . f
167 fmap f = fmapMaybe $ Just . f
577
168
578−instance Reflex t => FunctorMaybe (Event t) where
169+-- | Create a new 'Event' by combining each occurence with the next value
579− {-# INLINE fmapMaybe #-}
170+-- of the list using the supplied function. If the list runs out of items,
580− fmapMaybe f = push $ return . f
171+-- all subsequent 'Event' occurrences will be ignored.
581−
172+zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
582−-- | Never: @'zero' = 'never'@.
173+zipListWithEvent f l e = do
583−instance Reflex t => Plus (Event t) where
174+ rec lb <- hold l eTail
584− zero = never
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
585
183
586-- | Replace each occurrence value of the 'Event' with the value of the
184-- | Replace each occurrence value of the 'Event' with the value of the
587-- 'Behavior' at the time of that occurrence.
185-- 'Behavior' at the time of that occurrence.
588tag :: Reflex t => Behavior t b -> Event t a -> Event t b
186tag :: Reflex t => Behavior t b -> Event t a -> Event t b
589tag b = pushAlways $ \_ -> sample b
187tag b = pushAlways $ \_ -> sample b
590
188
591−-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
189+-- | Create a new 'Event' that combines occurences of supplied 'Event'
592−-- current value of the 'Behavior'.
190+-- with the current value of the 'Behavior'.
593attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
191attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
594attach = attachWith (,)
192attach = attachWith (,)
595
193
596−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
194+-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
597−-- combining it with the current value of the 'Behavior'.
195+-- by combining it with the current value of the 'Behavior'.
598attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
196attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
599attachWith f = attachWithMaybe $ \a b -> Just $ f a b
197attachWith f = attachWithMaybe $ \a b -> Just $ f a b
600
198
601−-- | Create a new 'Event' by combining each occurence with the current value of
199+-- | Create a new 'Event' by combining each occurence with the current
602−-- the 'Behavior'. The occurrence is discarded if the combining function returns
200+-- value of the 'Behavior'. The occurrence is discarded if the combining function
603−-- Nothing
201+-- returns Nothing
604attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
202attachWithMaybe :: 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
606
204
205+-- | Alias for 'headE'
206+onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
207+onceE = headE
208+
607−-- | Create a new 'Event' that only occurs only once, on the first occurence of
209+-- | Create a new 'Event' that only occurs on the first occurence of
608-- the supplied 'Event'.
210-- the supplied 'Event'.
609headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
211headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
610headE e = do
212headE e = do
611 rec be <- hold e $ fmap (const never) e'
213 rec be <- hold e $ fmap (const never) e'
612 let e' = switch be
214 let e' = switch be
215+ e' `seq` return ()
613 return e'
216 return e'
614
217
615−-- | Create a new 'Event' that occurs on all but the first occurence of the
218+-- | Create a new 'Event' that occurs on all but the first occurence
616−-- supplied 'Event'.
219+-- of the supplied 'Event'.
617tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
220tailE :: (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
619
222
620−-- | Create a tuple of two 'Event's with the first one occuring only the first
223+-- | Create a tuple of two 'Event's with the first one occuring only
621−-- time the supplied 'Event' occurs and the second occuring on all but the first
224+-- the first time the supplied 'Event' occurs and the second occuring
622−-- occurence.
225+-- on all but the first occurence.
623headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
226headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
624headTailE e = do
227headTailE e = do
625 eHead <- headE e
228 eHead <- headE e
626 be <- hold never $ fmap (const e) eHead
229 be <- hold never $ fmap (const e) eHead
627 return (eHead, switch be)
230 return (eHead, switch be)
628
231
629−-- | Split the supplied 'Event' into two individual 'Event's occuring at the
232+-- | Split the supplied 'Event' into two individual 'Event's occuring
630−-- same time with the respective values from the tuple.
233+-- at the same time with the respective values from the tuple.
631splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
234splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
632splitE e = (fmap fst e, fmap snd e)
235splitE e = (fmap fst e, fmap snd e)
633
236
634-- | Print the supplied 'String' and the value of the 'Event' on each
237-- | Print the supplied 'String' and the value of the 'Event' on each
635-- occurence. This should /only/ be used for debugging.
238-- occurence. This should /only/ be used for debugging.
636--
239--
637−-- Note: As with Debug.Trace.trace, the message will only be printed if the
240+-- Note: As with Debug.Trace.trace, the message will only be printed if
638−-- 'Event' is actually used.
241+-- the 'Event' is actually used.
639traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
242traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
640traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
243traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
641
244
642−-- | Print the output of the supplied function on each occurence of the
245+-- | Print the output of the supplied function on each occurence of
643−-- 'Event'. This should /only/ be used for debugging.
246+-- the 'Event'. This should /only/ be used for debugging.
644--
247--
645−-- Note: As with Debug.Trace.trace, the message will only be printed if the
248+-- Note: As with Debug.Trace.trace, the message will only be printed if
646−-- 'Event' is actually used.
249+-- the 'Event' is actually used.
647traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
250traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
648traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
251traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
649
252
650−instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
253+-- | Tag type for 'Either' to use it as a 'DSum'.
651− (<>) = alignWith (mergeThese (<>))
254+data EitherTag l r a where
652− sconcat = fmap sconcat . mergeList . toList
255+ LeftTag :: EitherTag l r l
653−#if MIN_VERSION_semigroups(0,17,0)
256+ RightTag :: EitherTag l r r
654− stimes n = fmap $ stimes n
257+
655−#else
258+instance GEq (EitherTag l r) where
656− times1p n = fmap $ times1p n
259+ geq a b = case (a, b) of
657−#endif
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
658
313
659instance (Semigroup a, Reflex t) => Monoid (Event t a) where
314instance (Semigroup a, Reflex t) => Monoid (Event t a) where
660 mempty = never
315 mempty = never
661− mappend = (<>)
316+ mappend a b = mconcat [a, b]
662 mconcat = fmap sconcat . mergeList
317 mconcat = fmap sconcat . mergeList
663
318
664−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
319+-- | Create a new 'Event' that occurs if at least one of the 'Event's
665−-- list occurs. If multiple occur at the same time they are folded from the left
320+-- in the list occurs. If multiple occur at the same time they are
666−-- with the given function.
321+-- folded from the left with the given function.
667−{-# INLINE mergeWith #-}
668mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
322mergeWith :: 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)
323+mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
674− . merge
324+ . merge
675− . DMap.fromDistinctAscList
325+ . DMap.fromDistinctAscList
676− . map (\(k, v) -> Const2 k :=> v)
326+ . map (\(k, v) -> (Const2 k) :=> v)
677− $ zip [0 :: Int ..] es
327+ $ zip [0 :: Int ..] es
678−
328+
679−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
329+-- | Create a new 'Event' that occurs if at least one of the 'Event's
680−-- list occurs. If multiple occur at the same time the value is the value of the
330+-- in the list occurs. If multiple occur at the same time the value is
681−-- leftmost event.
331+-- the value of the leftmost event.
682−{-# INLINE leftmost #-}
683leftmost :: Reflex t => [Event t a] -> Event t a
332leftmost :: Reflex t => [Event t a] -> Event t a
684leftmost = mergeWith const
333leftmost = mergeWith const
685
334
686−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
335+-- | Create a new 'Event' that occurs if at least one of the 'Event's
687−-- list occurs and has a list of the values of all 'Event's occuring at that
336+-- in the list occurs and has a list of the values of all 'Event's
688−-- time.
337+-- occuring at that time.
689mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
338mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
690mergeList [] = never
339mergeList [] = never
691−mergeList es = mergeWithFoldCheap' id es
340+mergeList es = mergeWith (<>) $ map (fmap (:|[])) 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
695
341
696−-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
342+-- | Create a new 'Event' combining the map of 'Event's into an
697−-- occurs if at least one of them occurs and has a map of values of all 'Event's
343+-- 'Event' that occurs if at least one of them occurs and has a map of
698−-- occuring at that time.
344+-- values of all 'Event's occuring at that time.
699mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
345mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
700mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
346mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
701
347
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
348+-- | Split the event into an 'EventSelector' that allows efficient
730−-- the individual 'Event's.
349+-- selection of the individual 'Event's.
731fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
350fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
732fanMap = fan . fmap mapToDMap
351fanMap = fan . fmap mapToDMap
733
352
734−-- | Switches to the new event whenever it receives one. Whenever a new event
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
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)
354+switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
738switchPromptly ea0 eea = do
355switchPromptly ea0 eea = do
739 bea <- hold ea0 eea
356 bea <- hold ea0 eea
740 let eLag = switch bea
357 let eLag = switch bea
741 eCoincidences = coincidence eea
358 eCoincidences = coincidence eea
742 return $ leftmost [eCoincidences, eLag]
359 return $ leftmost [eCoincidences, eLag]
743
360
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−
753instance Reflex t => Align (Event t) where
361instance Reflex t => Align (Event t) where
754 nil = never
362 nil = never
755− align = alignEventWithMaybe Just
363+ align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
756
364
757−-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
365+-- | Create a new 'Event' that only occurs if the supplied 'Event'
758−-- the 'Behavior' is true at the time of occurence.
366+-- occurs and the 'Behavior' is true at the time of occurence.
759gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
367gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
760gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
368gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
761
369
762−-- | Create a new behavior given a starting behavior and switch to a the behvior
370+-- | Create a new behavior given a starting behavior and switch to a the
763−-- carried by the event when it fires.
371+-- behvior carried by the event when it fires.
764switcher :: (Reflex t, MonadHold t m)
372switcher :: (Reflex t, MonadHold t m)
765 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
373 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
766switcher b eb = pull . (sample <=< sample) <$> hold b eb
374switcher 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 #-}
1+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, DataKinds, PolyKinds #-}
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
2+module Reflex.Dynamic ( Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
23− , current
3+ , current
24− , updated
4+ , updated
25− , holdDyn
26− , constDyn
5+ , constDyn
27− , uniqDyn
6+ , holdDyn
28− , uniqDynBy
7+ , nubDyn
29− , count
8+ , count
30− , toggle
9+ , toggle
31− , switchPromptlyDyn
10+ , switchPromptlyDyn
32− , tagPromptlyDyn
11+ , tagDyn
33− , attachPromptlyDyn
12+ , attachDyn
34− , attachPromptlyDynWith
13+ , attachDynWith
35− , attachPromptlyDynWithMaybe
14+ , attachDynWithMaybe
15+ , mapDyn
16+ , forDyn
17+ , mapDynM
36− , foldDyn
18+ , foldDyn
37− , foldDynM
19+ , foldDynM
38− , foldDynMaybe
20+ , foldDynMaybe
39− , foldDynMaybeM
21+ , foldDynMaybeM
22+ , combineDyn
23+ , collectDyn
24+ , mconcatDyn
25+ , distributeDMapOverDyn
26+ , joinDyn
40− , joinDynThroughMap
27+ , joinDynThroughMap
41− , traceDyn
28+ , traceDyn
42− , traceDynWith
29+ , traceDynWith
43− , splitDynPure
30+ , splitDyn
44− , distributeMapOverDynPure
45− , distributeDMapOverDynPure
46− , distributeListOverDynPure
47− , Demux
31+ , Demux
48− , demux
32+ , demux
49− , demuxed
33+ , getDemuxed
50− -- Things that probably aren't very useful:
34+ -- Things that probably aren't very useful:
51− , HList (..)
35+ , HList (..)
52− , FHList (..)
36+ , FHList (..)
53− , collectDynPure
37+ , distributeFHListOverDyn
54− , RebuildSortedHList (..)
55− , IsHList (..)
56− , AllAreFunctors (..)
57− , HListPtr (..)
58− , distributeFHListOverDynPure
59− -- Unsafe
38+ -- Unsafe
60− , unsafeDynamic
39+ , 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
40+ ) where
81
41
82import Prelude hiding (mapM, mapM_)
42import Prelude hiding (mapM, mapM_)
83
43
84−import Data.Functor.Misc
85import Reflex.Class
44import Reflex.Class
45+import Data.Functor.Misc
86
46
87−import Control.Applicative ((<*>))
88−import Control.Monad hiding (forM, forM_, mapM, mapM_)
47+import Control.Monad hiding (mapM, mapM_, forM, forM_)
89import Control.Monad.Fix
48import 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)
91import Data.Align
52import Data.Align
53+import Data.Map (Map)
54+import qualified Data.Map as Map
92import Data.Dependent.Map (DMap)
55import Data.Dependent.Map (DMap)
93import qualified Data.Dependent.Map as DMap
56import qualified Data.Dependent.Map as DMap
94import Data.Dependent.Sum (DSum (..))
57import Data.Dependent.Sum (DSum (..))
95−import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
58+import Data.GADT.Compare (GCompare (..), GEq (..), (:~:) (..), GOrdering (..))
96−import Data.Map (Map)
97import Data.Monoid
59import Data.Monoid
98−import Data.These
60+--import Data.HList (HList (..), hBuild)
99
61
100−import Debug.Trace
62+data HList (l::[*]) where
63+ HNil :: HList '[]
64+ HCons :: e -> HList l -> HList (e ': l)
101
65
102−-- | Create a new 'Dynamic' that only signals changes if the values actually
66+infixr 2 `HCons`
103−-- changed.
104−uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
105−uniqDyn = uniqDynBy (==)
106
67
107−-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
68+type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
108−-- changes and the given function returns 'False' when given both the old and
69+type instance HRevApp '[] l = l
109−-- the new values.
70+type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
110−uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
71+
111−uniqDynBy eq d =
72+hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
112− let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
73+hRevApp HNil l = l
113− in unsafeDynamic (current d) e'
74+hRevApp (HCons x l) l' = hRevApp l (HCons x l')
114
75
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+
115−-- | Create a 'Dynamic' using the initial value and change it each time the
162+-- | Create a 'Dynamic' using the initial value and change it each
116−-- 'Event' occurs using a folding function on the previous value and the value
163+-- time the 'Event' occurs using a folding function on the previous
117−-- of the 'Event'.
164+-- value and the value of the 'Event'.
118foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
165foldDyn :: (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
120
167
121−-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
168+-- | Create a 'Dynamic' using the initial value and change it each
122−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
169+-- time the 'Event' occurs using a monadic folding function on the
170+-- previous value and the value of the 'Event'.
123foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
171foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
124−foldDynM = accumM . flip
172+foldDynM f = foldDynMaybeM $ \o v -> liftM Just $ f o v
125−
173+
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.
132foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
174foldDynMaybe :: (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
134
176
135−-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
136−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
137foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
177foldDynMaybeM :: (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'
139
184
140-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
185-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
141count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
186count :: (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
146toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
191toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
147toggle = foldDyn (const not)
192toggle = foldDyn (const not)
148
193
149−-- | Switches to the new 'Event' whenever it receives one. Switching occurs
194+-- | Switches to the new 'Event' whenever it receives one. Switching
150−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
195+-- occurs *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the old and new
151−-- old and new inner Events fire simultaneously, the output will fire with the
196+-- inner Events fire simultaneously, the output will fire with the value of the *new* 'Event'.
152−-- value of the *new* 'Event'.
153switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
197switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
154switchPromptlyDyn de =
198switchPromptlyDyn de =
155 let eLag = switch $ current de
199 let eLag = switch $ current de
156 eCoincidences = coincidence $ updated de
200 eCoincidences = coincidence $ updated de
157 in leftmost [eCoincidences, eLag]
201 in leftmost [eCoincidences, eLag]
158
202
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+
159−-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
222+-- | Split the 'Dynamic' into two 'Dynamic's, each taking the
223+-- respective value of the tuple.
160−splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
224+splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
161−splitDynPure d = (fmap fst d, fmap snd d)
225+splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
162−
226+
163−-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
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
164−-- non-'Dynamic' elements.
234+-- Dynamic values.
165−distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
235+distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
166−distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
236+distributeDMapOverDyn dm = case DMap.toList dm of
167−
237+ [] -> return $ constDyn DMap.empty
168−-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
238+ [k :=> v] -> mapDyn (DMap.singleton k . Identity) v
169−-- non-'Dynamic' elements, preserving the order of the elements.
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'.
170−distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
284+joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
171−distributeListOverDynPure =
285+joinDyn dd =
172− fmap (map fromDSum . DMap.toAscList) .
286+ let b' = pull $ sample . current =<< sample (current dd)
173− distributeDMapOverDynPure .
287+ eOuter :: Event t a = pushAlways (sample . current) $ updated dd
174− DMap.fromDistinctAscList .
288+ eInner :: Event t a = switch $ fmap updated (current dd)
175− zipWith toDSum [0..]
289+ eBoth :: Event t a = coincidence $ fmap updated (updated dd)
176− where
290+ e' = leftmost [eBoth, eOuter, eInner]
177− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
291+ in Dynamic b' e'
178− toDSum k v = Const2 k :=> v
179− fromDSum :: DSum (Const2 Int a) Identity -> a
180− fromDSum (Const2 _ :=> Identity v) = v
181
292
182--TODO: Generalize this to functors other than Maps
293--TODO: Generalize this to functors other than Maps
183-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
294-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
184-- with the current values of the 'Dynamic's in a map.
295-- with the current values of the 'Dynamic's in a map.
185joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
296joinDynThroughMap :: 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
297+joinDynThroughMap dd =
187−
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+
188−-- | Print the value of the 'Dynamic' when it is first read and on each
310+-- | Print the value of the 'Dynamic' on each change and prefix it
189−-- subsequent change that is observed (as traceEvent), prefixed with the
190−-- provided string. This should /only/ be used for debugging.
311+-- with the provided string. This should /only/ be used for debugging.
191--
312--
192-- Note: Just like Debug.Trace.trace, the value will only be shown if something
313-- Note: Just like Debug.Trace.trace, the value will only be shown if something
193-- else in the system is depending on it.
314-- else in the system is depending on it.
@@ -195,8 +316,7 @@ traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
195traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
316traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
196
317
197-- | Print the result of applying the provided function to the value
318-- | 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
319+-- of the 'Dynamic' on each change. This should /only/ be used for
199−-- that is observed (as traceEvent). This should /only/ be used for
200-- debugging.
320-- debugging.
201--
321--
202-- Note: Just like Debug.Trace.trace, the value will only be shown if something
322-- 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
204traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
324traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
205traceDynWith f d =
325traceDynWith f d =
206 let e' = traceEventWith f $ updated d
326 let e' = traceEventWith f $ updated d
207− getV0 = do
208− x <- sample $ current d
327+ in Dynamic (current d) e'
209− trace (f x) $ return x
210− in unsafeBuildDynamic getV0 e'
211
328
212-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
329-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
213-- each time the 'Event' occurs.
330-- each time the 'Event' occurs.
214--
331--
215−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
332+-- Note: `tagDyn 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`
333+-- at the same time that `d` is changing. With `tagDyn d e`, the *new* value of `d`
217-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
334-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
218-- will be used, since the 'Behavior' won't be updated until the end of the frame.
335-- will be used, since the 'Behavior' won't be updated until the end of the frame.
219-- Additionally, this means that the output 'Event' may not be used to directly change
336-- Additionally, this means that the output 'Event' may not be used to directly change
220-- the input 'Dynamic', because that would mean its value depends on itself. When creating
337-- the input 'Dynamic', because that would mean its value depends on itself. When creating
221-- cyclic data flows, generally `tag (current d) e` is preferred.
338-- cyclic data flows, generally `tag (current d) e` is preferred.
222−tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
339+tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
223−tagPromptlyDyn = attachPromptlyDynWith const
340+tagDyn = attachDynWith const
224
341
225-- | Attach the current value of the 'Dynamic' to the value of the
342-- | Attach the current value of the 'Dynamic' to the value of the
226-- 'Event' each time it occurs.
343-- 'Event' each time it occurs.
227--
344--
228−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
345+-- Note: `attachDyn d` is not the same as `attach (current d)`. See 'tagDyn' for details.
229−attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
346+attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
230−attachPromptlyDyn = attachPromptlyDynWith (,)
347+attachDyn = attachDynWith (,)
231
348
232-- | Combine the current value of the 'Dynamic' with the value of the
349-- | Combine the current value of the 'Dynamic' with the value of the
233-- 'Event' each time it occurs.
350-- 'Event' each time it occurs.
234--
351--
235−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
352+-- Note: `attachDynWith f d` is not the same as `attachWith f (current d)`. See 'tagDyn' for details.
236−attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
353+attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
237−attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
354+attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
238
355
239−-- | Create a new 'Event' by combining the value at each occurence with the
356+-- | Create a new 'Event' by combining the value at each occurence
240−-- current value of the 'Dynamic' value and possibly filtering if the combining
357+-- with the current value of the 'Dynamic' value and possibly
241−-- function returns 'Nothing'.
358+-- filtering if the combining function returns 'Nothing'.
242--
359--
243−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
360+-- Note: `attachDynWithMaybe f d` is not the same as `attachWithMaybe f (current d)`. See 'tagDyn' for details.
244−-- (current d)`. See 'tagPromptlyDyn' for details.
245−attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
361+attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
246−attachPromptlyDynWithMaybe f d e =
362+attachDynWithMaybe f d e =
247 let e' = attach (current d) e
363 let e' = attach (current d) e
248 in fforMaybe (align e' $ updated d) $ \case
364 in fforMaybe (align e' $ updated d) $ \case
249 This (a, b) -> f a b -- Only the tagging event is firing, so use that
365 This (a, b) -> f a b -- Only the tagging event is firing, so use that
@@ -254,38 +370,36 @@ attachPromptlyDynWithMaybe f d e =
254-- Demux
370-- Demux
255--------------------------------------------------------------------------------
371--------------------------------------------------------------------------------
256
372
257−-- | Represents a time changing value together with an 'EventSelector' that can
373+-- | Represents a time changing value together with an 'EventSelector'
258−-- efficiently detect when the underlying 'Dynamic' has a particular value.
374+-- that can efficiently detect when the underlying Dynamic has a particular value.
259−-- This is useful for representing data like the current selection of a long
375+-- This is useful for representing data like the current selection of a long list.
260−-- list.
261--
376--
262-- Semantically,
377-- Semantically,
263−--
264−-- > demuxed (demux d) k === fmap (== k) d
378+-- > getDemuxed (demux d) k === mapDyn (== k) d
265−--
266−-- However, the when getDemuxed is used multiple times, the complexity is only
379+-- However, the when getDemuxed is used multiple times, the complexity is only /O(log(n))/,
267−-- /O(log(n))/, rather than /O(n)/ for fmap.
380+-- rather than /O(n)/ for mapDyn.
268data Demux t k = Demux { demuxValue :: Behavior t k
381data Demux t k = Demux { demuxValue :: Behavior t k
269 , demuxSelector :: EventSelector t (Const2 k Bool)
382 , demuxSelector :: EventSelector t (Const2 k Bool)
270 }
383 }
271
384
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'.
274demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
386demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
275demux k = Demux (current k)
387demux k = Demux (current k)
276− (fan $ attachWith (\k0 k1 -> if k0 == k1
388+ (fan $ attachWith (\k0 k1 -> if k0 == k1
277 then DMap.empty
389 then DMap.empty
278 else DMap.fromList [Const2 k0 :=> Identity False,
390 else DMap.fromList [Const2 k0 :=> Identity False,
279 Const2 k1 :=> Identity True])
391 Const2 k1 :=> Identity True])
280 (current k) (updated k))
392 (current k) (updated k))
281
393
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
282−-- | Select a particular output of the 'Demux'; this is equivalent to (but much
395+-- | Select a particular output of the 'Demux'; this is equivalent to (but much faster than)
283−-- faster than) mapping over the original 'Dynamic' and checking whether it is
396+-- mapping over the original 'Dynamic' and checking whether it is equal to the given key.
284−-- equal to the given key.
285−demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
397+getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
286−demuxed d k =
398+getDemuxed d k = do
287 let e = select (demuxSelector d) (Const2 k)
399 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
289
403
290--------------------------------------------------------------------------------
404--------------------------------------------------------------------------------
291-- collectDyn
405-- collectDyn
@@ -293,41 +407,6 @@ demuxed d k =
293
407
294--TODO: This whole section is badly in need of cleanup
408--TODO: This whole section is badly in need of cleanup
295
409
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.
331data FHList f l where
410data FHList f l where
332 FHNil :: FHList f '[]
411 FHNil :: FHList f '[]
333 FHCons :: f e -> FHList f l -> FHList f (e ': l)
412 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
344 HTailPtr _ `gcompare` HHeadPtr = GGT
423 HTailPtr _ `gcompare` HHeadPtr = GGT
345 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
424 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
346
425
347−-- | A typed index into a typed heterogeneous list.
348data HListPtr l a where
426data HListPtr l a where
349 HHeadPtr :: HListPtr (h ': t) h
427 HHeadPtr :: HListPtr (h ': t) h
350 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
428 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
@@ -354,10 +432,8 @@ fhlistToDMap = DMap.fromList . go
354 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
432 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
355 go = \case
433 go = \case
356 FHNil -> []
434 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)
358
436
359−-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
360−-- they must be contiguous and sorted.
361class RebuildSortedHList l where
437class RebuildSortedHList l where
362 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
438 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
363 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
439 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
@@ -381,14 +457,17 @@ instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
381dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
457dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
382dmapToHList = rebuildSortedHList . DMap.toList
458dmapToHList = rebuildSortedHList . DMap.toList
383
459
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)
460+distributeFHListOverDyn :: forall t m l. (Reflex t, MonadHold t m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
388−distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap 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+-}
389
470
390−-- | Indicates that all elements in a type-level list are applications of the
391−-- same functor.
392class AllAreFunctors (f :: a -> *) (l :: [a]) where
471class AllAreFunctors (f :: a -> *) (l :: [a]) where
393 type FunctorList f l :: [*]
472 type FunctorList f l :: [*]
394 toFHList :: HList (FunctorList f l) -> FHList f l
473 toFHList :: HList (FunctorList f l) -> FHList f l
@@ -398,32 +477,26 @@ instance AllAreFunctors f '[] where
398 type FunctorList f '[] = '[]
477 type FunctorList f '[] = '[]
399 toFHList l = case l of
478 toFHList l = case l of
400 HNil -> FHNil
479 HNil -> FHNil
401−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
402 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
480 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
403−#endif
404 fromFHList FHNil = HNil
481 fromFHList FHNil = HNil
405
482
406instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
483instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
407 type FunctorList f (h ': t) = f h ': FunctorList f t
484 type FunctorList f (h ': t) = f h ': FunctorList f t
408 toFHList l = case l of
485 toFHList l = case l of
409 a `HCons` b -> a `FHCons` toFHList b
486 a `HCons` b -> a `FHCons` toFHList b
410−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
411 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
487 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
412−#endif
413 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
488 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
414
489
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)
490+collectDyn :: ( RebuildSortedHList (HListElems b)
419− , IsHList a, IsHList b
491+ , IsHList a, IsHList b
420− , AllAreFunctors (Dynamic t) (HListElems b)
492+ , AllAreFunctors (Dynamic t) (HListElems b)
421− , Reflex t
493+ , Reflex t, MonadHold t m
422− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
494+ , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
423− ) => a -> Dynamic t b
495+ ) => a -> m (Dynamic t b)
496+collectDyn ds =
424−collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
497+ mapDyn fromHList =<< distributeFHListOverDyn (toFHList $ toHList ds)
425−
498+
426−-- | Poor man's 'Generic's for product types only.
499+-- Poor man's Generic
427class IsHList a where
500class IsHList a where
428 type HListElems a :: [*]
501 type HListElems a :: [*]
429 toHList :: a -> HList (HListElems a)
502 toHList :: a -> HList (HListElems a)
@@ -434,155 +507,18 @@ instance IsHList (a, b) where
434 toHList (a, b) = hBuild a b
507 toHList (a, b) = hBuild a b
435 fromHList l = case l of
508 fromHList l = case l of
436 a `HCons` b `HCons` HNil -> (a, b)
509 a `HCons` b `HCons` HNil -> (a, b)
437−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
438 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
510 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
439−#endif
440
511
441instance IsHList (a, b, c, d) where
512instance IsHList (a, b, c, d) where
442 type HListElems (a, b, c, d) = [a, b, c, d]
513 type HListElems (a, b, c, d) = [a, b, c, d]
443 toHList (a, b, c, d) = hBuild a b c d
514 toHList (a, b, c, d) = hBuild a b c d
444 fromHList l = case l of
515 fromHList l = case l of
445 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
516 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
446−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
447 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
517 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
448−#endif
449
518
450instance IsHList (a, b, c, d, e, f) where
519instance IsHList (a, b, c, d, e, f) where
451 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
520 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
452 toHList (a, b, c, d, e, f) = hBuild a b c d e f
521 toHList (a, b, c, d, e, f) = hBuild a b c d e f
453 fromHList l = case l of
522 fromHList l = case l of
454 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
523 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
455−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
456 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
524 _ -> 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 @@
5{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE TemplateHaskell #-}
6{-# LANGUAGE TemplateHaskell #-}
7{-# LANGUAGE TypeOperators #-}
7{-# LANGUAGE TypeOperators #-}
8−#ifdef USE_REFLEX_OPTIMIZER
9−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10−#endif
11−-- | Template Haskell helper functions for building complex 'Dynamic' values.
12−module Reflex.Dynamic.TH
8+module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
13− ( qDynPure
14− , unqDyn
15− , mkDynPure
16− -- * Deprecated functions
17− , qDyn
18− , mkDyn
19− ) where
20
9
21import Reflex.Dynamic
10import Reflex.Dynamic
22
11
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
24import Data.Data
15import Data.Data
25−import Data.Generics
16+import Control.Monad.State
26−import Data.Monoid
27import qualified Language.Haskell.Exts as Hs
17import qualified Language.Haskell.Exts as Hs
28import qualified Language.Haskell.Meta.Syntax.Translate as Hs
18import qualified Language.Haskell.Meta.Syntax.Translate as Hs
29−import Language.Haskell.TH
19+import Data.Monoid
30−import Language.Haskell.TH.Quote
20+import Data.Generics
31−import qualified Language.Haskell.TH.Syntax as TH
32
21
33−-- | Quote a 'Dynamic' expression. Within the quoted expression, you can use
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'
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
23+qDyn :: Q Exp -> Q Exp
37−qDynPure qe = do
24+qDyn qe = do
38 e <- qe
25 e <- qe
39 let f :: forall d. Data d => d -> StateT [(Name, Exp)] Q d
26 let f :: forall d. Data d => d -> StateT [(Name, Exp)] Q d
40 f d = case eqT of
27 f d = case eqT of
@@ -49,34 +36,21 @@ qDynPure qe = do
49 let exprs = reverse exprsReversed
36 let exprs = reverse exprsReversed
50 arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
37 arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
51 param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
38 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) |]
53
40
54−-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
55−-- 'qDyn' quotation.
56unqDyn :: Q Exp -> Q Exp
41unqDyn :: Q Exp -> Q Exp
57unqDyn e = [| unqMarker $e |]
42unqDyn e = [| unqMarker $e |]
58
43
59−-- | This type represents an occurrence of unqDyn before it has been processed
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.
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.
62data UnqDyn
45data UnqDyn
63
46
64−-- unqMarker must not be exported; it is used only as a way of smuggling data
47+-- unqMarker must not be exported; it is used only as a way of smuggling data from unqDyn to qDyn
65−-- from unqDyn to qDyn
66−
67--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
48--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
68unqMarker :: a -> UnqDyn
49unqMarker :: a -> UnqDyn
69unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
50unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
70
51
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
52+mkDyn :: QuasiQuoter
79−mkDynPure = QuasiQuoter
53+mkDyn = QuasiQuoter
80 { quoteExp = mkDynExp
54 { quoteExp = mkDynExp
81 , quotePat = error "mkDyn: pattern splices are not supported"
55 , quotePat = error "mkDyn: pattern splices are not supported"
82 , quoteType = error "mkDyn: type splices are not supported"
56 , quoteType = error "mkDyn: type splices are not supported"
@@ -86,7 +60,7 @@ mkDynPure = QuasiQuoter
86mkDynExp :: String -> Q Exp
60mkDynExp :: String -> Q Exp
87mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
61mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
88 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
62 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
90 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
64 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
91#if MIN_VERSION_haskell_src_exts(1,18,0)
65#if MIN_VERSION_haskell_src_exts(1,18,0)
92 antiE :: Hs.Exp Hs.SrcSpanInfo -> Hs.Exp Hs.SrcSpanInfo
66 antiE :: Hs.Exp Hs.SrcSpanInfo -> Hs.Exp Hs.SrcSpanInfo
@@ -107,24 +81,3 @@ mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ H
107 reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
81 reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
108 | modName == modName' && occName == occName' = 'unqMarker
82 | modName == modName' && occName == occName' = 'unqMarker
109 reinstateUnqDyn x = x
83 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 #-}
1+{-# LANGUAGE ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, FunctionalDependencies, FlexibleContexts #-}
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
2+module Reflex.Host.Class where
20− ( ReflexHost (..)
21− , MonadSubscribeEvent (..)
22− , MonadReadEvent (..)
23− , MonadReflexCreateTrigger (..)
24− , MonadReflexHost (..)
25− , fireEvents
26− , newEventWithTriggerRef
27− , fireEventRef
28− , fireEventRefAndRead
29− ) where
30
3
31import Reflex.Class
4import Reflex.Class
32
5
@@ -34,76 +7,49 @@ import Control.Applicative
34import Control.Monad
7import Control.Monad
35import Control.Monad.Fix
8import Control.Monad.Fix
36import Control.Monad.Identity
9import Control.Monad.Identity
37−import Control.Monad.Ref
38import Control.Monad.Trans
10import Control.Monad.Trans
39−import Control.Monad.Trans.Cont (ContT ())
11+import Control.Monad.Trans.Reader (ReaderT())
12+import Control.Monad.Trans.Writer (WriterT())
13+import Control.Monad.Trans.Cont (ContT())
40−import Control.Monad.Trans.Except (ExceptT ())
14+import Control.Monad.Trans.Except (ExceptT())
41−import Control.Monad.Trans.Reader (ReaderT ())
42−import Control.Monad.Trans.RWS (RWST ())
15+import Control.Monad.Trans.RWS (RWST())
43−import Control.Monad.Trans.State (StateT ())
16+import Control.Monad.Trans.State (StateT())
44import qualified Control.Monad.Trans.State.Strict as Strict
17import qualified Control.Monad.Trans.State.Strict as Strict
45−import Control.Monad.Trans.Writer (WriterT ())
46import Data.Dependent.Sum (DSum (..))
18import Data.Dependent.Sum (DSum (..))
47−import Data.GADT.Compare
48import Data.Monoid
19import Data.Monoid
49−
20+import Data.GADT.Compare
50−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
51−import Control.Monad.Primitive (touch)
21+import Control.Monad.Ref
52−import Data.IORef
53−import Reflex.Spider.Internal (EventM (..), HasSpiderTimeline, RootTrigger, SpiderEventHandle (..),
54− SpiderHost (..), SpiderHostFrame (..), Subscriber (..), newEventWithTriggerIO,
55− newFanEventWithTriggerIO, run, runFrame, scheduleClear, subscribe)
56−import qualified Reflex.Spider.Internal
57−#endif
58
22
59-- Note: this import must come last to silence warnings from AMP
23-- Note: this import must come last to silence warnings from AMP
60−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
24+import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
61−
25+
62−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
63−#include "../Spider/SpiderTimelineHost.include.hs"
64−#endif
65−
66−-- | Framework implementation support class for the reflex implementation
26+-- | Framework implementation support class for the reflex implementation represented by @t@.
67−-- 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
68−class ( Reflex t
69− , MonadReflexCreateTrigger t (HostFrame t)
70− , MonadSample t (HostFrame t)
71− , MonadHold t (HostFrame t)
72− , MonadFix (HostFrame t)
73− , MonadSubscribeEvent t (HostFrame t)
74− ) => ReflexHost t where
75 type EventTrigger t :: * -> *
28 type EventTrigger t :: * -> *
76 type EventHandle t :: * -> *
29 type EventHandle t :: * -> *
77 type HostFrame t :: * -> *
30 type HostFrame t :: * -> *
78
31
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
82class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
33class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
83 -- | Subscribe to an event and set it up if needed.
34 -- | Subscribe to an event and set it up if needed.
84 --
35 --
85− -- This function will create a new 'EventHandle' from an 'Event'. This handle
36+ -- This function will create a new 'EventHandle' from an 'Event'. This handle may then be used via
86− -- may then be used via 'readEvent' in the read callback of
37+ -- 'readEvent' in the read callback of 'fireEventsAndRead'.
87− -- 'fireEventsAndRead'.
88 --
38 --
89− -- If the event wasn't subscribed to before (either manually or through a
39+ -- If the event wasn't subscribed to before (either manually or through a dependent event or behavior)
90− -- dependent event or behavior) then this function will cause the event and
40+ -- then this function will cause the event and all dependencies of this event to be set up.
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.
41+ -- For example, if the event was created by 'newEventWithTrigger', then it's callback will be executed.
93 --
42 --
94 -- It's safe to call this function multiple times.
43 -- It's safe to call this function multiple times.
95 subscribeEvent :: Event t a -> m (EventHandle t a)
44 subscribeEvent :: Event t a -> m (EventHandle t a)
96
45
97-- | Monad that allows to read events' values.
46-- | Monad that allows to read events' values.
98class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
47class (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
48+ -- | Read the value of an 'Event' from an 'EventHandle' (created by calling 'subscribeEvent').
100− -- 'subscribeEvent').
101 --
49 --
102− -- After event propagation is done, all events can be in two states: either
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.
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
51+ -- In the former case, this function returns @Just act@, where @act@ in an action to read the current value of
105− -- the current value of the event. In the latter case, the function returns
52+ -- the event. In the latter case, the function returns @Nothing@.
106− -- @Nothing@.
107 --
53 --
108 -- This function is normally used in the calllback for 'fireEventsAndRead'.
54 -- This function is normally used in the calllback for 'fireEventsAndRead'.
109 readEvent :: EventHandle t a -> m (Maybe (m a))
55 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
112class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
58class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
113 -- | Creates a root 'Event' (one that is not based on any other event).
59 -- | Creates a root 'Event' (one that is not based on any other event).
114 --
60 --
115− -- When a subscriber first subscribes to an event (building another event that
61+ -- When a subscriber first subscribes to an event (building another event
116− -- depends on the subscription) the given callback function is run and passed
62+ -- that depends on the subscription) the given callback function is run and
117− -- a trigger. The callback function can then set up the event source in IO.
63+ -- passed 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
64+ -- After this is done, the callback function must return an accompanying teardown action.
119− -- teardown action.
120 --
65 --
121− -- Any time between setup and teardown the trigger can be used to fire the
66+ -- Any time between setup and teardown the trigger can be used to fire
122− -- event, by passing it to 'fireEventsAndRead'.
67+ -- the event, by passing it to 'fireEventsAndRead'.
123 --
68 --
124− -- Note: An event may be set up multiple times. So after the teardown action
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.
125− -- is executed, the event may still be set up again in the future.
126 newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
70 newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
127 newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
71 newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
128
72
129−-- | 'MonadReflexHost' designates monads that can run reflex frames.
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
130−class ( ReflexHost t
131− , MonadReflexCreateTrigger t m
132− , MonadSubscribeEvent t m
133− , MonadReadEvent t (ReadPhase m)
134− , MonadSample t (ReadPhase m)
135− , MonadHold t (ReadPhase m)
136− ) => MonadReflexHost t m | m -> t where
137 type ReadPhase m :: * -> *
74 type ReadPhase m :: * -> *
138 -- | Propagate some events firings and read the values of events afterwards.
75 -- | Propagate some events firings and read the values of events afterwards.
139 --
76 --
140− -- This function will create a new frame to fire the given events. It will
77+ -- This function will create a new frame to fire the given events. It will then update all
141− -- then update all dependent events and behaviors. After that is done, the
78+ -- dependent events and behaviors. After that is done, the given callback is executed which
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.
79+ -- allows to read the final values of events and check whether they have fired in this frame or
80+ -- not.
144 --
81 --
145 -- All events that are given are fired at the same time.
82 -- All events that are given are fired at the same time.
146 --
83 --
147− -- This function is typically used in the main loop of a reflex framework
84+ -- This function is typically used in the main loop of a reflex framework implementation.
148− -- implementation. The main loop waits for external events to happen (such as
85+ -- The main loop waits for external events to happen (such as keyboard input or a mouse click)
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
86+ -- and then fires the corresponding events using this function. The read callback can be used
151− -- and perform a corresponding response action to the external event.
87+ -- to read output events and perform a corresponding response action to the external event.
152− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
88+ fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> (ReadPhase m a) -> m a
153
89
154 -- | Run a frame without any events firing.
90 -- | Run a frame without any events firing.
155 --
91 --
156− -- This function should be used when you want to use 'sample' and 'hold' when
92+ -- This function should be used when you want to use 'sample' and 'hold' when no events are currently firing.
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
93+ -- Using this function in that case can improve performance, since the implementation can assume that no events
159− -- firing when 'sample' or 'hold' are called.
94+ -- are firing when 'sample' or 'hold' are called.
160 --
95 --
161− -- This function is commonly used to set up the basic event network when the
96+ -- This function is commonly used to set up the basic event network when the application starts up.
162− -- application starts up.
163 runHostFrame :: HostFrame t a -> m a
97 runHostFrame :: HostFrame t a -> m a
164
98
165-- | Like 'fireEventsAndRead', but without reading any events.
99-- | Like 'fireEventsAndRead', but without reading any events.
@@ -169,13 +103,10 @@ fireEvents dm = fireEventsAndRead dm $ return ()
169
103
170-- | Create a new event and store its trigger in an 'IORef' while it's active.
104-- | Create a new event and store its trigger in an 'IORef' while it's active.
171--
105--
172−-- An event is only active between the set up (when it's first subscribed to)
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).
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
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
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
108+-- trigger this event. When the event is not active, the 'IORef' will contain 'Nothing'. This allows event sources to be more efficient,
177−-- sources to be more efficient, since they don't need to produce events when
109+-- since they don't need to produce events when nobody is listening.
178−-- nobody is listening.
179newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
110newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
180newEventWithTriggerRef = do
111newEventWithTriggerRef = do
181 rt <- newRef Nothing
112 rt <- newRef Nothing
@@ -185,7 +116,6 @@ newEventWithTriggerRef = do
185 return (e, rt)
116 return (e, rt)
186{-# INLINE newEventWithTriggerRef #-}
117{-# INLINE newEventWithTriggerRef #-}
187
118
188−-- | Fire the given trigger if it is not 'Nothing'.
189fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
119fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
190fireEventRef mtRef input = do
120fireEventRef mtRef input = do
191 mt <- readRef mtRef
121 mt <- readRef mtRef
@@ -193,8 +123,6 @@ fireEventRef mtRef input = do
193 Nothing -> return ()
123 Nothing -> return ()
194 Just trigger -> fireEvents [trigger :=> Identity input]
124 Just trigger -> fireEvents [trigger :=> Identity input]
195
125
196−-- | Fire the given trigger if it is not 'Nothing', and read from the given
197−-- 'EventHandle'.
198fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
126fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
199fireEventRefAndRead mtRef input e = do
127fireEventRefAndRead mtRef input e = do
200 mt <- readRef mtRef
128 mt <- readRef mtRef
@@ -204,7 +132,7 @@ fireEventRefAndRead mtRef input e = do
204 mGetValue <- readEvent e
132 mGetValue <- readEvent e
205 case mGetValue of
133 case mGetValue of
206 Nothing -> return Nothing
134 Nothing -> return Nothing
207− Just getValue -> fmap Just getValue
135+ Just getValue -> liftM Just getValue
208
136
209
137
210--------------------------------------------------------------------------------
138--------------------------------------------------------------------------------
@@ -246,8 +174,8 @@ instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
246 type ReadPhase (StateT s m) = ReadPhase m
174 type ReadPhase (StateT s m) = ReadPhase m
247 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
175 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
248 runHostFrame = lift . runHostFrame
176 runHostFrame = lift . runHostFrame
249−
177+
250−
178+
251instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
179instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
252 newEventWithTrigger = lift . newEventWithTrigger
180 newEventWithTrigger = lift . newEventWithTrigger
253 newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
181 newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
@@ -258,9 +186,9 @@ instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) wh
258instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
186instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
259 type ReadPhase (Strict.StateT s m) = ReadPhase m
187 type ReadPhase (Strict.StateT s m) = ReadPhase m
260 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
188 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
261− runHostFrame = lift . runHostFrame
189+ runHostFrame = lift . runHostFrame
262−
190+
263−
191+
264
192
265instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
193instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
266 newEventWithTrigger = lift . newEventWithTrigger
194 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
1+module Reflex.Spider ( Spider
5− ( Spider
2+ , SpiderHost (..) --Temporary, until Widget no longer uses unsafePerformIO
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
3+ ) where
18
4
19−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
20−import Reflex.Class
21−#endif
22import Reflex.Spider.Internal
5import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hs+1095−1838
@@ -1,191 +1,83 @@
1−{-# LANGUAGE BangPatterns #-}
1+{-# LANGUAGE CPP, ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, LambdaCase, TypeOperators #-}
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
2+module Reflex.Spider.Internal where
30−
3+
31−import Control.Applicative
4+import qualified Reflex.Class as R
32−import Control.Concurrent
5+import qualified Reflex.Host.Class as R
33−import Control.Exception
6+
7+import Data.IORef
8+import System.Mem.Weak
9+import Data.Foldable
10+import Data.Traversable
34−import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
11+import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
35−import Control.Monad.Exception
36−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
12+import Control.Monad.Identity hiding (mapM, mapM_, forM_, forM, sequence)
37−import Control.Monad.Primitive
38−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
13+import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
39−import Control.Monad.Ref
14+import GHC.Exts
40−import Data.Align
15+import Control.Applicative -- Unconditionally import, because otherwise it breaks on GHC 7.10.1RC2
41−import Data.Coerce
42import Data.Dependent.Map (DMap, DSum (..))
16import Data.Dependent.Map (DMap, DSum (..))
43import qualified Data.Dependent.Map as DMap
17import 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
49import Data.GADT.Compare
18import 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')
54import Data.Maybe
19import Data.Maybe
20+import Data.IntMap (IntMap)
21+import qualified Data.IntMap as IntMap
22+import Control.Monad.Ref
23+import Control.Monad.Exception
55import Data.Monoid ((<>))
24import Data.Monoid ((<>))
56−import Data.Proxy
25+import Data.Coerce
57−import Data.These
26+
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
64import System.IO.Unsafe
27import System.IO.Unsafe
65−import System.Mem.Weak
66import Unsafe.Coerce
28import Unsafe.Coerce
67−
68−#ifdef DEBUG_CYCLES
69−import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
29+import Control.Monad.Primitive
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
88
30
89-- Note: must come last to silence warnings due to AMP on GHC < 7.10
31-- Note: must come last to silence warnings due to AMP on GHC < 7.10
90−import Prelude hiding (any, concat, mapM, mapM_, sequence)
32+import Prelude hiding (mapM, mapM_, any, sequence, concat)
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
105
33
106debugPropagate :: Bool
34debugPropagate :: Bool
107
35
108debugInvalidateHeight :: Bool
36debugInvalidateHeight :: Bool
109
37
110−debugInvalidate :: Bool
111−
112#ifdef DEBUG
38#ifdef DEBUG
113
39
114#define DEBUG_NODEIDS
40#define DEBUG_NODEIDS
115
41
116debugPropagate = True
42debugPropagate = True
117
43
118−debugInvalidateHeight = False
44+debugInvalidateHeight = True
119−
120−debugInvalidate = True
121
45
122class HasNodeId a where
46class HasNodeId a where
123 getNodeId :: a -> Int
47 getNodeId :: a -> Int
124
48
125−instance HasNodeId (Hold x p a) where
49+instance HasNodeId (Hold a) where
126 getNodeId = holdNodeId
50 getNodeId = holdNodeId
127
51
128−instance HasNodeId (PushSubscribed x a b) where
52+instance HasNodeId (PushSubscribed a b) where
129 getNodeId = pushSubscribedNodeId
53 getNodeId = pushSubscribedNodeId
130
54
131−instance HasNodeId (SwitchSubscribed x a) where
55+instance HasNodeId (SwitchSubscribed a) where
132 getNodeId = switchSubscribedNodeId
56 getNodeId = switchSubscribedNodeId
133
57
58+instance HasNodeId (MergeSubscribed a) where
59+ getNodeId = mergeSubscribedNodeId
60+
134−instance HasNodeId (FanSubscribed x a) where
61+instance HasNodeId (FanSubscribed a) where
135 getNodeId = fanSubscribedNodeId
62 getNodeId = fanSubscribedNodeId
136
63
137−instance HasNodeId (CoincidenceSubscribed x a) where
64+instance HasNodeId (CoincidenceSubscribed a) where
138 getNodeId = coincidenceSubscribedNodeId
65 getNodeId = coincidenceSubscribedNodeId
139
66
140−instance HasNodeId (RootSubscribed x a) where
67+instance HasNodeId (RootSubscribed a) where
141 getNodeId = rootSubscribedNodeId
68 getNodeId = rootSubscribedNodeId
142
69
143−instance HasNodeId (Pull x a) where
144− getNodeId = pullNodeId
145−
146−{-# INLINE showNodeId #-}
147showNodeId :: HasNodeId a => a -> String
70showNodeId :: HasNodeId a => a -> String
148showNodeId = ("#"<>) . show . getNodeId
71showNodeId = ("#"<>) . show . getNodeId
149
72
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−
177#else
73#else
178
74
179debugPropagate = False
75debugPropagate = False
180
76
181debugInvalidateHeight = False
77debugInvalidateHeight = False
182
78
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 #-}
187showNodeId :: a -> String
79showNodeId :: a -> String
188−showNodeId _ = ""
80+showNodeId = const ""
189
81
190#endif
82#endif
191
83
@@ -194,922 +86,377 @@ showNodeId _ = ""
194nextNodeIdRef :: IORef Int
86nextNodeIdRef :: IORef Int
195nextNodeIdRef = unsafePerformIO $ newIORef 1
87nextNodeIdRef = unsafePerformIO $ newIORef 1
196
88
197−newNodeId :: IO Int
198−newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
199−
200{-# NOINLINE unsafeNodeId #-}
89{-# NOINLINE unsafeNodeId #-}
201unsafeNodeId :: a -> Int
90unsafeNodeId :: a -> Int
202unsafeNodeId a = unsafePerformIO $ do
91unsafeNodeId a = unsafePerformIO $ do
203 touch a
92 touch a
204− newNodeId
93+ atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
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 $
272#endif
94#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
696
95
96+--TODO: Figure out why certain things are not 'representational', then make them representational so we can use coerce
697--type role Hold representational
97--type role Hold representational
698−data Hold x p
98+data Hold a
699− = Hold { holdValue :: !(IORef (PatchTarget p))
99+ = Hold { holdValue :: !(IORef a)
700− , holdInvalidators :: !(IORef [Weak (Invalidator x)])
100+ , holdInvalidators :: !(IORef [Weak Invalidator])
701− , holdEvent :: Event x p -- This must be lazy, or holds cannot be defined before their input Events
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
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
103+ , holdParent :: !(IORef Any) -- Keeps its parent alive (will be undefined until the hold is initialized --TODO: Probably shouldn't be an IORef
703#ifdef DEBUG_NODEIDS
104#ifdef DEBUG_NODEIDS
704 , holdNodeId :: Int
105 , holdNodeId :: Int
705#endif
106#endif
706 }
107 }
707
108
708−-- | A statically allocated 'SpiderTimeline'; this is the only instance of
109+data EventEnv
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
110+ = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment])
735− , eventEnvHoldInits :: !(IORef [SomeHoldInit x]) -- Needed for Subscribe
111+ , eventEnvHoldInits :: !(IORef [SomeHoldInit])
736− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
737− , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
738− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
112+ , eventEnvClears :: !(IORef [SomeMaybeIORef])
739− , eventEnvRootClears :: !(IORef [SomeRootClear])
113+ , eventEnvRootClears :: !(IORef [SomeDMapIORef])
740− , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
114+ , eventEnvCurrentHeight :: !(IORef Int)
741− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
115+ , eventEnvCoincidenceInfos :: !(IORef [SomeCoincidenceInfo])
742− , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
116+ , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
743 }
117 }
744
118
745−{-# INLINE runEventM #-}
746−runEventM :: EventM x a -> IO a
119+runEventM :: EventM a -> EventEnv -> IO a
747−runEventM = unEventM
120+runEventM = runReaderT . unEventM
748−
121+
749−asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
122+askToAssignRef :: EventM (IORef [SomeAssignment])
750−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
123+askToAssignRef = EventM $ asks eventEnvAssignments
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
802
124
803−instance HasSpiderTimeline Global where
125+askHoldInitRef :: EventM (IORef [SomeHoldInit])
804− spiderTimeline = globalSpiderTimelineEnv
126+askHoldInitRef = EventM $ asks eventEnvHoldInits
805
127
806−putCurrentHeight :: HasSpiderTimeline x => Height -> EventM x ()
128+getCurrentHeight :: EventM Int
807−putCurrentHeight h = do
129+getCurrentHeight = EventM $ do
808− heightRef <- asksEventEnv eventEnvCurrentHeight
130+ heightRef <- asks eventEnvCurrentHeight
809− liftIO $ writeIORef heightRef $! h
131+ liftIO $ readIORef heightRef
810
132
811−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
133+putCurrentHeight :: Int -> EventM ()
812− {-# INLINE getDeferralQueue #-}
134+putCurrentHeight h = EventM $ do
813− getDeferralQueue = asksEventEnv eventEnvClears
135+ heightRef <- asks eventEnvCurrentHeight
136+ liftIO $ writeIORef heightRef h
814
137
815−{-# INLINE scheduleClear #-}
816−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
138+scheduleClear :: IORef (Maybe a) -> EventM ()
817−scheduleClear r = defer $ SomeClear r
139+scheduleClear r = EventM $ do
140+ clears <- asks eventEnvClears
141+ liftIO $ modifyIORef' clears (SomeMaybeIORef r :)
818
142
819−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
143+scheduleRootClear :: IORef (DMap k Identity) -> EventM ()
820− {-# INLINE getDeferralQueue #-}
144+scheduleRootClear r = EventM $ do
821− getDeferralQueue = asksEventEnv eventEnvRootClears
145+ clears <- asks eventEnvRootClears
146+ liftIO $ modifyIORef' clears (SomeDMapIORef r :)
822
147
823−{-# INLINE scheduleRootClear #-}
148+scheduleMerge :: Int -> MergeSubscribed a -> EventM ()
824−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
149+scheduleMerge height subscribed = EventM $ do
825−scheduleRootClear r = defer $ SomeRootClear r
150+ delayedRef <- asks eventEnvDelayedMerges
151+ liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) height [DelayedMerge subscribed]
826
152
827−instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
153+emitCoincidenceInfo :: SomeCoincidenceInfo -> EventM ()
828− {-# INLINE getDeferralQueue #-}
154+emitCoincidenceInfo sci = EventM $ do
829− getDeferralQueue = asksEventEnv eventEnvResetCoincidences
155+ ciRef <- asks eventEnvCoincidenceInfos
156+ liftIO $ modifyIORef' ciRef (sci:)
830
157
831-- Note: hold cannot examine its event until after the phase is over
158-- Note: hold cannot examine its event until after the phase is over
832−{-# INLINE [1] hold #-}
159+hold :: a -> Event a -> EventM (Behavior a)
833−hold :: (Patch p, Defer (SomeHoldInit x) m) => PatchTarget p -> Event x p -> m (Hold x p)
834hold v0 e = do
160hold v0 e = do
161+ holdInitRef <- askHoldInitRef
162+ liftIO $ do
835− valRef <- liftIO $ newIORef v0
163+ valRef <- newIORef v0
836− invsRef <- liftIO $ newIORef []
164+ invsRef <- newIORef []
837− parentRef <- liftIO $ newIORef Nothing
165+ parentRef <- newIORef $ error "hold not yet initialized (parent)"
838−#ifdef DEBUG_NODEIDS
166+ subscriberRef <- newIORef $ error "hold not yet initialized (subscriber)"
839− nodeId <- liftIO newNodeId
840−#endif
841− let h = Hold
167+ let h = Hold
842− { holdValue = valRef
168+ { holdValue = valRef
843− , holdInvalidators = invsRef
169+ , holdInvalidators = invsRef
844− , holdEvent = e
170+ , holdSubscriber = subscriberRef
845− , holdParent = parentRef
171+ , holdParent = parentRef
846#ifdef DEBUG_NODEIDS
172#ifdef DEBUG_NODEIDS
847− , holdNodeId = nodeId
173+ , holdNodeId = unsafeNodeId (v0, e)
848#endif
174#endif
849− }
175+ }
850− defer $ SomeHoldInit h
176+ s <- newSubscriberHold h
851− return h
177+ writeIORef subscriberRef $ unsafeCoerce s
852−
178+ modifyIORef' holdInitRef (SomeHoldInit e h :)
853−{-# INLINE getHoldEventSubscription #-}
179+ return $ BehaviorHold h
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)
180+
855−getHoldEventSubscription h = do
181+subscribeHold :: Event a -> Hold a -> EventM ()
856− ep <- liftIO $ readIORef $ holdParent h
182+subscribeHold e h = do
857− case ep of
183+ toAssignRef <- askToAssignRef
858− Just subd -> return subd
184+ !s <- liftIO $ liftM unsafeCoerce $ readIORef $ holdSubscriber h -- This must be performed strictly so that the weak pointer points at the actual item
859− Nothing -> do
185+ ws <- liftIO $ mkWeakPtrWithDebug s "holdSubscriber"
860− let e = holdEvent h
186+ subd <- subscribe e $ WeakSubscriberSimple ws
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
187+ liftIO $ writeIORef (holdParent h) $ unsafeCoerce subd
876− return subscription
188+ occ <- liftIO $ getEventSubscribedOcc subd
877−
189+ case occ of
878−type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
190+ Nothing -> return ()
191+ Just o -> liftIO $ modifyIORef' toAssignRef (SomeAssignment h o :)
879
192
880--type role BehaviorM representational
193--type role BehaviorM representational
881-- BehaviorM can sample behaviors
194-- BehaviorM can sample behaviors
882−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
195+newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed])) IO a } deriving (Functor, Applicative, Monad, 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
893
196
894−data BehaviorSubscribed x a
197+data BehaviorSubscribed a
895− = forall p. BehaviorSubscribedHold (Hold x p)
198+ = BehaviorSubscribedHold (Hold a)
896− | BehaviorSubscribedPull (PullSubscribed x a)
199+ | BehaviorSubscribedPull (PullSubscribed a)
897
200
898−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
201+data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
899
202
900--type role PullSubscribed representational
203--type role PullSubscribed representational
901−data PullSubscribed x a
204+data PullSubscribed a
902 = PullSubscribed { pullSubscribedValue :: !a
205 = PullSubscribed { pullSubscribedValue :: !a
903− , pullSubscribedInvalidators :: !(IORef [Weak (Invalidator x)])
206+ , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
904− , pullSubscribedOwnInvalidator :: !(Invalidator x)
207+ , pullSubscribedOwnInvalidator :: !Invalidator
905− , pullSubscribedParents :: ![SomeBehaviorSubscribed x] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
208+ , pullSubscribedParents :: ![SomeBehaviorSubscribed] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
906 }
209 }
907
210
908--type role Pull representational
211--type role Pull representational
909−data Pull x a
212+data Pull a
910− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed x a)))
213+ = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
911− , pullCompute :: !(BehaviorM x a)
214+ , pullCompute :: !(BehaviorM a)
912−#ifdef DEBUG_NODEIDS
913− , pullNodeId :: Int
914−#endif
915 }
215 }
916
216
917−data Invalidator x
217+data Invalidator
918− = forall a. InvalidatorPull (Pull x a)
218+ = forall a. InvalidatorPull (Pull a)
919− | forall a. InvalidatorSwitch (SwitchSubscribed x a)
219+ | forall a. InvalidatorSwitch (SwitchSubscribed 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− }
932
220
221+data RootSubscribed a
222+ = RootSubscribed { rootSubscribedSubscribers :: !(IORef [WeakSubscriber a])
223+ , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
224+ }
225+
933−data Root x (k :: * -> *)
226+data Root (k :: * -> *)
934 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
227 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
935− , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
228+ , rootSubscribed :: !(IORef (DMap k RootSubscribed))
936− , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
229+ , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
937 }
230 }
938
231
939−data SomeHoldInit x = forall p. Patch p => SomeHoldInit !(Hold x p)
232+data SomeHoldInit = forall a. SomeHoldInit (Event a) (Hold a)
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 () }
948
233
949-- EventM can do everything BehaviorM can, plus create holds
234-- 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
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
951−
236+
952−newtype ComputeM x a = ComputeM (ReaderT (IORef [SomeHoldInit x]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
237+data PushSubscribed a b
953−
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)
954−instance Defer (SomeHoldInit x) (ComputeM x) where
239+ , pushSubscribedHeight :: !(IORef Int)
955− getDeferralQueue = ComputeM ask
240+ , pushSubscribedSubscribers :: !(IORef [WeakSubscriber b])
956−
241+ , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die
957−{-# INLINABLE runComputeM #-}
242+ , pushSubscribedParent :: !(EventSubscribed a)
958−runComputeM :: Defer (SomeHoldInit x) m => ComputeM x a -> m a
243+#ifdef DEBUG_NODEIDS
959−runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
244+ , pushSubscribedNodeId :: Int
960−
245+#endif
961−newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
246+ }
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
992
247
993−heightBagMax :: HeightBag -> Height
248+data Push a b
994−heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
249+ = Push { pushCompute :: !(a -> EventM (Maybe b)) -- Compute the current firing value; assumes that its parent has been computed
995− Just ((h, _), _) -> Height h
250+ , pushParent :: !(Event a)
996− Nothing -> zeroHeight
251+ , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
252+ }
997
253
998−heightBagVerify :: HeightBag -> HeightBag
254+data MergeSubscribed k
999−#ifdef DEBUG
255+ = MergeSubscribed { mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1000−heightBagVerify b@(HeightBag s c) = if
256+ , mergeSubscribedAccum :: !(IORef (DMap k Identity)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
1001− | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
257+ , mergeSubscribedHeight :: !(IORef Int)
1002− -> error $ "heightBagVerify: size doesn't match: " <> show b
258+ , mergeSubscribedSubscribers :: !(IORef [WeakSubscriber (DMap k Identity)])
1003− | unHeight invalidHeight `IntMap.member` c
259+ , mergeSubscribedSelf :: !Any -- Hold all our Subscribers in memory
1004− -> error $ "heightBagVerify: contains invalid height: " <> show b
260+ , mergeSubscribedParents :: !(DMap k EventSubscribed)
1005− | otherwise -> b
261+#ifdef DEBUG_NODEIDS
1006−#else
262+ , mergeSubscribedNodeId :: Int
1007−heightBagVerify = id
1008#endif
263#endif
264+ }
1009
265
1010−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
266+--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
1011− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
267+data Merge k
1012− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
268+ = Merge { mergeParents :: !(DMap k Event)
1013− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
269+ , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k))) --TODO: Can we replace this with an unsafePerformIO thunk?
1014− }
270+ }
1015−
271+
1016−data FanSubscribed (x :: *) k
272+data FanSubscriberKey k a where
1017− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
273+ FanSubscriberKey :: k a -> FanSubscriberKey k [WeakSubscriber a]
1018− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
274+
1019− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
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))
1020− , fanSubscribedParent :: !(EventSubscription x)
288+ , fanSubscribedParent :: !(EventSubscribed (DMap k Identity))
289+ , fanSubscribedSelf :: {-# NOUNPACK #-} (Subscriber (DMap k Identity))
1021#ifdef DEBUG_NODEIDS
290#ifdef DEBUG_NODEIDS
1022 , fanSubscribedNodeId :: Int
291 , fanSubscribedNodeId :: Int
1023#endif
292#endif
1024 }
293 }
1025
294
1026−data Fan x k
295+data Fan k
1027− = Fan { fanParent :: !(Event x (DMap k Identity))
296+ = Fan { fanParent :: !(Event (DMap k Identity))
1028− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
297+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
1029 }
298 }
1030
299
1031−data SwitchSubscribed x a
300+data SwitchSubscribed a
1032− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1033− , switchSubscribedOccurrence :: !(IORef (Maybe a))
301+ = SwitchSubscribed { switchSubscribedOccurrence :: !(IORef (Maybe a))
1034− , switchSubscribedHeight :: !(IORef Height)
302+ , switchSubscribedHeight :: !(IORef Int)
1035− , switchSubscribedSubscribers :: !(WeakBag (Subscriber x a))
303+ , switchSubscribedSubscribers :: !(IORef [WeakSubscriber a])
304+ , switchSubscribedSelf :: {-# NOUNPACK #-} (Subscriber a)
305+ , switchSubscribedSelfWeak :: !(IORef (Weak (Subscriber a)))
1036− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !(Invalidator x)
306+ , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} Invalidator
1037− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak (Invalidator x)))
307+ , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
1038− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed x])
308+ , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
1039− , switchSubscribedParent :: !(Behavior x (Event x a))
309+ , switchSubscribedParent :: !(Behavior (Event a))
1040− , switchSubscribedCurrentParent :: !(IORef (EventSubscription x))
310+ , switchSubscribedCurrentParent :: !(IORef (EventSubscribed a))
1041− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed x a)))
1042#ifdef DEBUG_NODEIDS
311#ifdef DEBUG_NODEIDS
1043 , switchSubscribedNodeId :: Int
312 , switchSubscribedNodeId :: Int
1044#endif
313#endif
1045 }
314 }
1046
315
1047−data Switch x a
316+data Switch a
1048− = Switch { switchParent :: !(Behavior x (Event x a))
317+ = Switch { switchParent :: !(Behavior (Event a))
1049− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
318+ , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
1050 }
319 }
1051
320
1052−#ifdef USE_TEMPLATE_HASKELL
1053−{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
1054−#endif
1055−data CoincidenceSubscribed x a
321+data CoincidenceSubscribed a
1056− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1057− , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
322+ = CoincidenceSubscribed { coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
1058− , coincidenceSubscribedSubscribers :: !(WeakBag (Subscriber x a))
323+ , coincidenceSubscribedSubscribers :: !(IORef [WeakSubscriber a])
1059− , coincidenceSubscribedHeight :: !(IORef Height)
324+ , coincidenceSubscribedHeight :: !(IORef Int)
1060− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber x (Event x a))
325+ , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
1061− , coincidenceSubscribedOuterParent :: !(EventSubscription x)
326+ , coincidenceSubscribedOuterParent :: !(EventSubscribed (Event a))
1062− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed x)))
327+ , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
1063− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed x a)))
1064#ifdef DEBUG_NODEIDS
328#ifdef DEBUG_NODEIDS
1065 , coincidenceSubscribedNodeId :: Int
329 , coincidenceSubscribedNodeId :: Int
1066#endif
330#endif
1067 }
331 }
1068
332
1069−data Coincidence x a
333+data Coincidence a
1070− = Coincidence { coincidenceParent :: !(Event x (Event x a))
334+ = Coincidence { coincidenceParent :: !(Event (Event a))
1071− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
335+ , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
1072 }
336 }
1073
337
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+
1074{-# NOINLINE newInvalidatorSwitch #-}
443{-# NOINLINE newInvalidatorSwitch #-}
1075−newInvalidatorSwitch :: SwitchSubscribed x a -> IO (Invalidator x)
444+newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
1076newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
445newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1077
446
1078{-# NOINLINE newInvalidatorPull #-}
447{-# NOINLINE newInvalidatorPull #-}
1079−newInvalidatorPull :: Pull x a -> IO (Invalidator x)
448+newInvalidatorPull :: Pull a -> IO Invalidator
1080newInvalidatorPull p = return $! InvalidatorPull p
449newInvalidatorPull p = return $! InvalidatorPull p
1081
450
1082−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
451+{-# NOINLINE newBox #-}
1083− fmapMaybe f = push $ return . f
452+newBox :: a -> IO (Box a)
1084−
453+newBox a = return $! Box a
1085−instance HasSpiderTimeline x => Align (Event x) where
454+
1086− nil = eventNever
455+--type role Behavior representational
1087− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
456+data Behavior a
1088−
457+ = BehaviorHold !(Hold a)
1089−newtype Dyn x p = Dyn { unDyn :: IORef (Either (BehaviorM x (PatchTarget p), Event x p) (Hold x p)) }
458+ | BehaviorConst !a
1090−
459+ | BehaviorPull !(Pull a)
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')
1113
460
1114-- ResultM can read behaviors and events
461-- ResultM can read behaviors and events
1115type ResultM = EventM
462type ResultM = EventM
@@ -1118,410 +465,566 @@ type ResultM = EventM
1118unsafeNewIORef :: a -> b -> IORef b
465unsafeNewIORef :: a -> b -> IORef b
1119unsafeNewIORef _ b = unsafePerformIO $ newIORef b
466unsafeNewIORef _ b = unsafePerformIO $ newIORef b
1120
467
1121−instance HasSpiderTimeline x => Functor (Event x) where
468+instance Functor Event where
1122 fmap f = push $ return . Just . f
469 fmap f = push $ return . Just . f
1123
470
1124−instance Functor (Behavior x) where
471+instance Functor Behavior where
1125− fmap f = pull . fmap f . readBehaviorTracked
472+ fmap f = pull . liftM f . readBehaviorTracked
1126
473
1127−{-# INLINE push #-}
474+{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
1128−push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
475+push :: (a -> EventM (Maybe b)) -> Event a -> Event b
1129−push f e = cacheEvent (pushCheap f e)
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 #-}
1130
482
1131−{-# INLINABLE pull #-}
483+{-# NOINLINE pull #-}
1132−pull :: BehaviorM x a -> Behavior x a
484+pull :: BehaviorM a -> Behavior a
1133−pull a = behaviorPull $ Pull
485+pull a = BehaviorPull $ Pull
1134 { pullCompute = a
486 { pullCompute = a
1135 , pullValue = unsafeNewIORef a Nothing
487 , pullValue = unsafeNewIORef a Nothing
1136−#ifdef DEBUG_NODEIDS
1137− , pullNodeId = unsafeNodeId a
1138−#endif
1139 }
488 }
489+{-# RULES "pull/pull" forall a. pull (readBehaviorTracked (pull a)) = pull a #-}
1140
490
1141−{-# INLINABLE switch #-}
491+{-# NOINLINE switch #-}
1142−switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
492+switch :: Behavior (Event a) -> Event a
1143−switch a = eventSwitch $ Switch
493+switch a = EventSwitch $ Switch
1144 { switchParent = a
494 { switchParent = a
1145 , switchSubscribed = unsafeNewIORef a Nothing
495 , switchSubscribed = unsafeNewIORef a Nothing
1146 }
496 }
497+{-# RULES "switch/constB" forall e. switch (BehaviorConst e) = e #-}
1147
498
1148−coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
499+coincidence :: Event (Event a) -> Event a
1149−coincidence a = eventCoincidence $ Coincidence
500+coincidence a = EventCoincidence $ Coincidence
1150 { coincidenceParent = a
501 { coincidenceParent = a
1151 , coincidenceSubscribed = unsafeNewIORef a Nothing
502 , coincidenceSubscribed = unsafeNewIORef a Nothing
1152 }
503 }
1153
504
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+
1154-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
522-- 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
1156run roots after = do
524run roots after = do
1157− tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
525+ when debugPropagate $ putStrLn "Running an event frame"
1158− t <- SpiderHost ask
526+ result <- runFrame $ do
1159− result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock t) $ \_ -> flip runReaderT t $ unSpiderHost $ runFrame $ do
1160 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
527 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1161 occBefore <- liftIO $ do
528 occBefore <- liftIO $ do
1162 occBefore <- readIORef occRef
529 occBefore <- readIORef occRef
1163− writeIORef occRef $! DMap.insert k a occBefore
530+ writeIORef occRef $ DMap.insert k a occBefore
1164 return occBefore
531 return occBefore
1165 if DMap.null occBefore
532 if DMap.null occBefore
1166 then do scheduleRootClear occRef
533 then do scheduleRootClear occRef
1167 return $ Just r
534 return $ Just r
1168 else return Nothing
535 else return Nothing
1169 forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
536 forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
1170− propagate a subscribersRef
537+ propagateAndUpdateSubscribersRef subscribersRef a
1171− delayedRef <- asksEventEnv eventEnvDelayedMerges
538+ delayedRef <- EventM $ asks eventEnvDelayedMerges
1172 let go = do
539 let go = do
1173 delayed <- liftIO $ readIORef delayedRef
540 delayed <- liftIO $ readIORef delayedRef
1174 case IntMap.minViewWithKey delayed of
541 case IntMap.minViewWithKey delayed of
1175 Nothing -> return ()
542 Nothing -> return ()
1176− Just ((currentHeight, cur), future) -> do
543+ Just ((currentHeight, current), future) -> do
1177− tracePropagate (Proxy :: Proxy x) $ "Running height " ++ show currentHeight
544+ when debugPropagate $ liftIO $ putStrLn $ "Running height " ++ show currentHeight
1178− putCurrentHeight $ Height currentHeight
545+ putCurrentHeight currentHeight
1179− liftIO $ writeIORef delayedRef $! future
546+ liftIO $ writeIORef delayedRef future
1180− sequence_ cur
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
1181 go
560 go
1182 go
561 go
1183 putCurrentHeight maxBound
562 putCurrentHeight maxBound
1184 after
563 after
1185− tracePropagate (Proxy :: Proxy x) "Done running an event frame"
564+ when debugPropagate $ putStrLn "Done running an event frame"
1186 return result
565 return result
1187
566
1188−scheduleMerge' :: HasSpiderTimeline x => Height -> IORef Height -> EventM x () -> EventM x ()
567+data SomeMaybeIORef = forall a. SomeMaybeIORef (IORef (Maybe a))
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
1196
568
1197−data SomeClear = forall a. SomeClear (IORef (Maybe a))
569+data SomeDMapIORef = forall k. SomeDMapIORef (IORef (DMap k Identity))
1198
570
1199−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
571+data SomeAssignment = forall a. SomeAssignment (Hold a) a
1200
572
1201−data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
573+data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
1202
574
1203debugFinalize :: Bool
575debugFinalize :: Bool
1204debugFinalize = False
576debugFinalize = False
1205
577
1206mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
578mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
1207−mkWeakPtrWithDebug x debugNote = do
579+mkWeakPtrWithDebug x debugNote = mkWeakPtr x $
1208− x' <- evaluate x
1209− mkWeakPtr x' $
1210− if debugFinalize
580+ if debugFinalize
1211− then Just $ putStrLn $ "finalizing: " ++ debugNote
581+ then Just $ putStrLn $ "finalizing: " ++ debugNote
1212− else Nothing
582+ else Nothing
1213
583
1214type WeakList a = [Weak a]
584type WeakList a = [Weak a]
1215
585
1216−{-# INLINE withIncreasedDepth #-}
586+--TODO: Is it faster to clean up every time, or to occasionally go through and clean up as needed?
1217−#ifdef DEBUG
587+traverseAndCleanWeakList_ :: Monad m => (wa -> m (Maybe a)) -> [wa] -> (a -> m ()) -> m [wa]
1218−withIncreasedDepth :: CanTrace x m => m a -> m a
588+traverseAndCleanWeakList_ deRef ws f = go ws
1219−withIncreasedDepth a = do
589+ where go [] = return []
1220− spiderTimeline <- askSpiderTimelineEnv
590+ go (h:t) = do
1221− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
591+ ma <- deRef h
1222− result <- a
592+ case ma of
1223− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
593+ Just a -> do
1224− return result
594+ f a
1225−#else
595+ t' <- go t
1226−withIncreasedDepth :: m a -> m a
596+ return $ h : t'
1227−withIncreasedDepth = id
597+ Nothing -> go t
1228−#endif
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))
1229
675
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
1231
677
1232−{-# INLINE tracePropagate #-}
678+subscribeCoincidenceInner :: Event a -> Int -> CoincidenceSubscribed a -> EventM (Maybe a, Int, EventSubscribed a)
1233−tracePropagate :: (CanTrace x m) => proxy x -> String -> m ()
679+subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
1234−tracePropagate p = traceWhen p debugPropagate
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)
1235
688
1236−{-# INLINE traceInvalidate #-}
689+readBehavior :: Behavior a -> IO a
1237−traceInvalidate :: String -> IO ()
690+readBehavior b = runBehaviorM (readBehaviorTracked b) Nothing --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
1238−traceInvalidate = when debugInvalidate . liftIO . putStrLn
1239
691
1240−{-# INLINE traceWhen #-}
692+runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IO a
1241−traceWhen :: (CanTrace x m) => proxy x -> Bool -> String -> m ()
693+runBehaviorM a mwi = runReaderT (unBehaviorM a) mwi
1242−traceWhen p b message = traceMWhen p b $ return message
1243
694
1244−{-# INLINE traceMWhen #-}
695+askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
1245−traceMWhen :: (CanTrace x m) => proxy x -> Bool -> m String -> m ()
696+askInvalidator = liftM (fmap fst) $ BehaviorM ask
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
1272
697
1273−{-# INLINE propagateSubscriberHold #-}
698+askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
1274−propagateSubscriberHold :: forall x p. (HasSpiderTimeline x, Patch p) => Hold x p -> p -> EventM x ()
699+askParentsRef = liftM (fmap snd) $ BehaviorM ask
1275−propagateSubscriberHold h a = do
700+
1276− {-# SCC "trace" #-} traceMWhen (Proxy :: Proxy x) debugPropagate $ liftIO $ do
701+readBehaviorTracked :: Behavior a -> BehaviorM a
702+readBehaviorTracked b = case b of
703+ BehaviorHold h -> do
1277− invalidators <- liftIO $ readIORef $ holdInvalidators h
704+ result <- liftIO $ readIORef $ holdValue h
1278− return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
705+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
1279− v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
706+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
1280− case {-# SCC "apply" #-} apply a v of
707+ liftIO $ touch $ holdSubscriber h
1281− Nothing -> return ()
708+ return result
1282− Just v' -> do
709+ BehaviorConst a -> return a
1283− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate (Proxy :: Proxy x) $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
710+ BehaviorPull p -> do
1284− vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
711+ val <- liftIO $ readIORef $ pullValue p
1285− iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
712+ case val of
1286− defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
713+ Just subscribed -> do
1287−
714+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
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
715+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
1289−
716+ liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
1290−runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
717+ return $ pullSubscribedValue subscribed
1291−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
718+ Nothing -> do
1292−
719+ i <- liftIO $ newInvalidatorPull p
1293−askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
720+ wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
1294−askInvalidator = do
721+ parentsRef <- liftIO $ newIORef []
1295− (!m, _) <- BehaviorM ask
722+ a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
1296− case m of
723+ invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
1297− Nothing -> return Nothing
724+ parents <- liftIO $ readIORef parentsRef
1298− Just (!wi, _) -> return $ Just wi
725+ let subscribed = PullSubscribed
1299−
726+ { pullSubscribedValue = a
1300−askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
727+ , pullSubscribedInvalidators = invsRef
1301−askParentsRef = do
728+ , pullSubscribedOwnInvalidator = i
1302− (!m, _) <- BehaviorM ask
729+ , pullSubscribedParents = parents
1303− case m of
730+ }
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
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
1323− return h
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
1324
768
1325-- Always refers to 0
769-- Always refers to 0
1326{-# NOINLINE zeroRef #-}
770{-# NOINLINE zeroRef #-}
1327−zeroRef :: IORef Height
771+zeroRef :: IORef Int
1328−zeroRef = unsafePerformIO $ newIORef zeroHeight
772+zeroRef = unsafePerformIO $ newIORef 0
1329−
773+
1330−type CanSubscribe x m =
774+getEventSubscribed :: Event a -> EventM (EventSubscribed a)
1331− ( HasSpiderTimeline x
775+getEventSubscribed e = case e of
1332− , HasCurrentHeight x m
776+ EventRoot k r -> liftM EventSubscribedRoot $ getRootSubscribed k r
1333− , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
777+ EventNever -> return EventSubscribedNever
1334− , Defer SomeClear m -- Wiping out event firings
778+ EventPush p -> liftM EventSubscribedPush $ getPushSubscribed p
1335− , Defer (SomeAssignment x) m -- Updating state
779+ EventFan k f -> liftM (EventSubscribedFan k) $ getFanSubscribed f
1336− , Defer (SomeResetCoincidence x) m
780+ EventMerge m -> liftM EventSubscribedMerge $ getMergeSubscribed m
1337− , m ~ EventM x
781+ EventSwitch s -> liftM EventSubscribedSwitch $ getSwitchSubscribed s
1338− )
782+ EventCoincidence c -> liftM EventSubscribedCoincidence $ getCoincidenceSubscribed c
1339−
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+
1340−getRootSubscribed :: GCompare k => k a -> Root x k -> Subscriber x a -> IO (WeakBagTicket, RootSubscribed x a, Maybe a)
844+getRootSubscribed :: GCompare k => k a -> Root k -> EventM (RootSubscribed a)
1341−getRootSubscribed k r sub = do
845+getRootSubscribed k r = do
1342− mSubscribed <- readIORef $ rootSubscribed r
846+ mSubscribed <- liftIO $ readIORef $ rootSubscribed r
1343− let getOcc = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1344 case DMap.lookup k mSubscribed of
847 case DMap.lookup k mSubscribed of
1345− Just subscribed -> {-# SCC "hitRoot" #-} do
848+ Just subscribed -> return subscribed
1346− sln <- subscribeRootSubscribed subscribed sub
849+ Nothing -> liftIO $ do
1347− occ <- getOcc
850+ subscribersRef <- newIORef []
1348− return (sln, subscribed, occ)
851+ subscribed <- newRootSubscribed (liftM (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r) subscribersRef
1349− Nothing -> {-# SCC "missRoot" #-} do
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
1350− weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
853+ uninit <- rootInit r k $ RootTrigger (subscribersRef, rootOccurrence r, k)
1351− let !cached = rootSubscribed r
854+ addFinalizer subscribed $ do
1352− uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
855+ when noinlineFalse $ putStr "" -- For some reason, without this line, the finalizer will run earlier than it should
1353− (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
856+-- putStrLn "Uninit root"
1354− when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
857+ uninit
1355− uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
858+ liftIO $ modifyIORef' (rootSubscribed r) $ DMap.insert k subscribed
1356− writeIORef uninitRef $! uninit
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 []
1357− let !subscribed = RootSubscribed
877+ let subscribed = PushSubscribed
1358− { rootSubscribedKey = k
878+ { pushSubscribedOccurrence = occRef
1359− , rootSubscribedCachedSubscribed = cached
879+ , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
1360− , rootSubscribedOccurrence = getOcc
880+ , pushSubscribedSubscribers = subscribersRef
1361− , rootSubscribedSubscribers = subs
881+ , pushSubscribedSelf = unsafeCoerce s
1362− , rootSubscribedUninit = uninit
882+ , pushSubscribedParent = subd
1363− , rootSubscribedWeakSelf = weakSelf
1364#ifdef DEBUG_NODEIDS
883#ifdef DEBUG_NODEIDS
1365− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
884+ , pushSubscribedNodeId = unsafeNodeId p
1366#endif
885#endif
1367 }
886 }
1368− -- If we die at the same moment that all our children die, they will
887+ liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
1369− -- try to clean us up but will fail because their Weak reference to us
888+ return subscribed
1370− -- will also be dead. So, if we are dying, check if there are any
889+
1371− -- children; since children don't bother cleaning themselves up if
890+getMergeSubscribed :: forall k. GCompare k => Merge k -> EventM (MergeSubscribed k)
1372− -- their parents are already dead, I don't think there's a race
891+getMergeSubscribed m = {-# SCC "getMergeSubscribed.entire" #-} do
1373− -- condition here. However, if there are any children, then we can
892+ mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
1374− -- infer that we need to clean ourselves up, so we do.
893+ case mSubscribed of
1375− finalCleanup = do
894+ Just subscribed -> return subscribed
1376− cs <- readIORef $ _weakBag_children subs
895+ Nothing -> if DMap.null $ mergeParents m then emptyMergeSubscribed else do
1377− when (not $ IntMap.null cs) (cleanupRootSubscribed subscribed)
896+ subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
1378− writeIORef weakSelf =<< evaluate =<< mkWeakPtr subscribed (Just finalCleanup)
897+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
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
898+ s <- liftIO $ newBox subscribedUnsafe
1380− occ <- getOcc
899+ ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberMerge"
1381− return (sln, subscribed, occ)
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
1382−
901+ parentSubd <- {-# SCC "getMergeSubscribed.a.parentSubd" #-} subscribe e $ WeakSubscriberMerge k ws
1383−#ifdef USE_TEMPLATE_HASKELL
902+ parentOcc <- {-# SCC "getMergeSubscribed.a.parentOcc" #-} liftIO $ getEventSubscribedOcc parentSubd
1384−{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
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
1385#endif
931#endif
1386−cleanupRootSubscribed :: RootSubscribed x a -> IO ()
932+ }
1387−cleanupRootSubscribed self@RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached } = do
933+ liftIO $ writeIORef subscribedRef subscribed
1388− rootSubscribedUninit self
934+ return subscribed
1389− modifyIORef' cached $ DMap.delete k
935+ where emptyMergeSubscribed = do --TODO: This should never happen
1390−
936+ occRef <- liftIO $ newIORef Nothing
1391−{-# INLINE subscribeRootSubscribed #-}
937+ accumRef <- liftIO $ newIORef DMap.empty
1392−subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
938+ subsRef <- liftIO $ newIORef []
1393−subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
939+ return $ MergeSubscribed
1394−
940+ { mergeSubscribedOccurrence = occRef
1395−{-# INLINABLE getFanSubscribed #-}
941+ , mergeSubscribedAccum = accumRef
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)
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)
1397−getFanSubscribed k f sub = do
952+getFanSubscribed f = do
1398 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
953 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1399 case mSubscribed of
954 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)
955+ Just subscribed -> return subscribed
1404− Nothing -> {-# SCC "missFan" #-} do
956+ Nothing -> do
1405− subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1406− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
957+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ fanSubscribed f
1407− s <- liftIO $ newSubscriberFan subscribedUnsafe
958+ sub <- liftIO $ newSubscriberFan subscribedUnsafe
1408− (subscription, parentOcc) <- subscribeAndRead (fanParent f) s
959+ wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberFan"
1409− weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
960+ subd <- subscribe (fanParent f) $ WeakSubscriberSimple wsub
1410− (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1411− subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
961+ subscribersRef <- liftIO $ newIORef DMap.empty
1412− occRef <- liftIO $ newIORef parentOcc
1413− when (isJust parentOcc) $ scheduleClear occRef
1414 let subscribed = FanSubscribed
962 let subscribed = FanSubscribed
1415− { fanSubscribedCachedSubscribed = fanSubscribed f
1416− , fanSubscribedOccurrence = occRef
1417− , fanSubscribedParent = subscription
963+ { fanSubscribedParent = subd
1418 , fanSubscribedSubscribers = subscribersRef
964 , fanSubscribedSubscribers = subscribersRef
965+ , fanSubscribedSelf = sub
1419#ifdef DEBUG_NODEIDS
966#ifdef DEBUG_NODEIDS
1420 , fanSubscribedNodeId = unsafeNodeId f
967 , fanSubscribedNodeId = unsafeNodeId f
1421#endif
968#endif
1422 }
969 }
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
1427 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
970 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1428− return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
971+ return subscribed
1429−
972+
1430−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
973+getSwitchSubscribed :: Switch a -> EventM (SwitchSubscribed a)
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
974+getSwitchSubscribed s = do
1457 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
975 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1458 case mSubscribed of
976 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)
977+ Just subscribed -> return subscribed
1463− Nothing -> {-# SCC "missSwitch" #-} do
978+ Nothing -> do
1464 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
979 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1465 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
980 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1466 i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
981 i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1467− mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
982+ sub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1468 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
983 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1469 wiRef <- liftIO $ newIORef wi
984 wiRef <- liftIO $ newIORef wi
985+ wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberSwitch"
986+ selfWeakRef <- liftIO $ newIORef wsub
1470 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
987 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
988+ e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) $ Just (wi, parentsRef)
1473− (subscription@(EventSubscription _ subd), parentOcc) <- subscribeAndRead e mySub
989+ subd <- subscribe e $ WeakSubscriberSimple wsub
1474− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
990+ subdRef <- liftIO $ newIORef subd
1475− subscriptionRef <- liftIO $ newIORef subscription
991+ parentOcc <- liftIO $ getEventSubscribedOcc subd
1476 occRef <- liftIO $ newIORef parentOcc
992 occRef <- liftIO $ newIORef parentOcc
1477 when (isJust parentOcc) $ scheduleClear occRef
993 when (isJust parentOcc) $ scheduleClear occRef
1478− weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
994+ heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
1479− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
995+ subscribersRef <- liftIO $ newIORef []
1480− let !subscribed = SwitchSubscribed
996+ let subscribed = SwitchSubscribed
1481− { switchSubscribedCachedSubscribed = switchSubscribed s
1482− , switchSubscribedOccurrence = occRef
997+ { switchSubscribedOccurrence = occRef
1483 , switchSubscribedHeight = heightRef
998 , switchSubscribedHeight = heightRef
1484− , switchSubscribedSubscribers = subs
999+ , switchSubscribedSubscribers = subscribersRef
1000+ , switchSubscribedSelf = sub
1001+ , switchSubscribedSelfWeak = selfWeakRef
1485 , switchSubscribedOwnInvalidator = i
1002 , switchSubscribedOwnInvalidator = i
1486 , switchSubscribedOwnWeakInvalidator = wiRef
1003 , switchSubscribedOwnWeakInvalidator = wiRef
1487 , switchSubscribedBehaviorParents = parentsRef
1004 , switchSubscribedBehaviorParents = parentsRef
1488 , switchSubscribedParent = switchParent s
1005 , switchSubscribedParent = switchParent s
1489− , switchSubscribedCurrentParent = subscriptionRef
1006+ , switchSubscribedCurrentParent = subdRef
1490− , switchSubscribedWeakSelf = weakSelf
1491#ifdef DEBUG_NODEIDS
1007#ifdef DEBUG_NODEIDS
1492 , switchSubscribedNodeId = unsafeNodeId s
1008 , switchSubscribedNodeId = unsafeNodeId s
1493#endif
1009#endif
1494 }
1010 }
1495− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1496− liftIO $ writeIORef subscribedRef $! subscribed
1011+ liftIO $ writeIORef subscribedRef subscribed
1497 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1012 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1498− return (slnForSub, subscribed, parentOcc)
1013+ return subscribed
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
1505
1014
1506−{-# INLINE subscribeSwitchSubscribed #-}
1015+getCoincidenceSubscribed :: forall a. Coincidence a -> EventM (CoincidenceSubscribed a)
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
1016+getCoincidenceSubscribed c = do
1513 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1017 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1514 case mSubscribed of
1018 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)
1019+ Just subscribed -> return subscribed
1519− Nothing -> {-# SCC "missCoincidence" #-} do
1020+ Nothing -> do
1520 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1021 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1521 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1022 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1522 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1023 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1523− (outerSubscription@(EventSubscription _ outerSubd), outerOcc) <- subscribeAndRead (coincidenceParent c) subOuter
1024+ wsubOuter <- liftIO $ mkWeakPtrWithDebug subOuter "subOuter"
1025+ outerSubd <- subscribe (coincidenceParent c) $ WeakSubscriberSimple wsubOuter
1026+ outerOcc <- liftIO $ getEventSubscribedOcc outerSubd
1524− outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1027+ outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
1525 (occ, height, mInnerSubd) <- case outerOcc of
1028 (occ, height, mInnerSubd) <- case outerOcc of
1526 Nothing -> return (Nothing, outerHeight, Nothing)
1029 Nothing -> return (Nothing, outerHeight, Nothing)
1527 Just o -> do
1030 Just o -> do
@@ -1532,452 +1035,270 @@ getCoincidenceSubscribed c sub = do
1532 heightRef <- liftIO $ newIORef height
1035 heightRef <- liftIO $ newIORef height
1533 innerSubdRef <- liftIO $ newIORef mInnerSubd
1036 innerSubdRef <- liftIO $ newIORef mInnerSubd
1534 scheduleClear innerSubdRef
1037 scheduleClear innerSubdRef
1535− weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1038+ subscribersRef <- liftIO $ newIORef []
1536− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1537 let subscribed = CoincidenceSubscribed
1039 let subscribed = CoincidenceSubscribed
1538− { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1539− , coincidenceSubscribedOccurrence = occRef
1040+ { coincidenceSubscribedOccurrence = occRef
1540 , coincidenceSubscribedHeight = heightRef
1041 , coincidenceSubscribedHeight = heightRef
1541− , coincidenceSubscribedSubscribers = subs
1042+ , coincidenceSubscribedSubscribers = subscribersRef
1542 , coincidenceSubscribedOuter = subOuter
1043 , coincidenceSubscribedOuter = subOuter
1543− , coincidenceSubscribedOuterParent = outerSubscription
1044+ , coincidenceSubscribedOuterParent = outerSubd
1544 , coincidenceSubscribedInnerParent = innerSubdRef
1045 , coincidenceSubscribedInnerParent = innerSubdRef
1545− , coincidenceSubscribedWeakSelf = weakSelf
1546#ifdef DEBUG_NODEIDS
1046#ifdef DEBUG_NODEIDS
1547 , coincidenceSubscribedNodeId = unsafeNodeId c
1047 , coincidenceSubscribedNodeId = unsafeNodeId c
1548#endif
1048#endif
1549 }
1049 }
1550− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1551− liftIO $ writeIORef subscribedRef $! subscribed
1050+ liftIO $ writeIORef subscribedRef subscribed
1552 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1051 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1553− return (slnForSub, subscribed, occ)
1052+ return subscribed
1554−
1053+
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)
1054+merge :: GCompare k => DMap k Event -> Event (DMap k Identity)
1566−merge d = cacheEvent (mergeCheap d)
1055+merge m = EventMerge $ Merge
1567−
1056+ { mergeParents = m
1568−{-# INLINE mergeWithMove #-}
1057+ , mergeSubscribed = unsafeNewIORef m Nothing
1569−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1058+ }
1570−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1059+
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 }
1060+newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
1784−
1061+
1785−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
1062+fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
1786fan e =
1063fan e =
1787 let f = Fan
1064 let f = Fan
1788 { fanParent = e
1065 { fanParent = e
1789 , fanSubscribed = unsafeNewIORef e Nothing
1066 , fanSubscribed = unsafeNewIORef e Nothing
1790 }
1067 }
1791− in EventSelector $ \k -> eventFan k f
1068+ in EventSelector $ \k -> EventFan k f
1792−
1069+
1793−runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeMergeInit x] -> EventM x ()
1070+-- | Run an event action outside of a frame
1794−runHoldInits holdInitRef mergeInitRef = do
1071+runFrame :: EventM a -> IO a
1795− holdInits <- liftIO $ readIORef holdInitRef
1072+runFrame a = do
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
1809 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1073 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1810 holdInitRef <- newIORef []
1074 holdInitRef <- newIORef []
1811− mergeUpdateRef <- newIORef []
1812− mergeInitRef <- newIORef []
1813− heightRef <- newIORef zeroHeight
1075+ heightRef <- newIORef 0
1814 toClearRef <- newIORef []
1076 toClearRef <- newIORef []
1815 toClearRootRef <- newIORef []
1077 toClearRootRef <- newIORef []
1816 coincidenceInfosRef <- newIORef []
1078 coincidenceInfosRef <- newIORef []
1817− delayedRef <- newIORef IntMap.empty
1079+ delayedRef <- liftIO $ 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
1080+ result <- flip runEventM (EventEnv toAssignRef holdInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) $ do
1822− writeIORef toAssignRef []
1081+ result <- a
1823− writeIORef holdInitRef []
1082+ let runHoldInits = do
1824− writeIORef mergeUpdateRef []
1083+ holdInits <- liftIO $ readIORef holdInitRef
1825− writeIORef mergeInitRef []
1084+ if null holdInits then return () else do
1826− writeIORef heightRef zeroHeight
1085+ liftIO $ writeIORef holdInitRef []
1827− writeIORef toClearRef []
1086+ forM_ holdInits $ \(SomeHoldInit e h) -> subscribeHold e h
1828− writeIORef toClearRootRef []
1087+ runHoldInits
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
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
1839− return result
1089+ return result
1840− result <- runEventM go
1841− toClear <- readIORef $ eventEnvClears env
1090+ toClear <- readIORef toClearRef
1842− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1091+ forM_ toClear $ \(SomeMaybeIORef ref) -> writeIORef ref Nothing
1843− toClearRoot <- readIORef $ eventEnvRootClears env
1092+ toClearRoot <- readIORef toClearRootRef
1844− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1093+ forM_ toClearRoot $ \(SomeDMapIORef ref) -> writeIORef ref DMap.empty
1845− toAssign <- readIORef $ eventEnvAssignments env
1094+ toAssign <- readIORef toAssignRef
1846 toReconnectRef <- newIORef []
1095 toReconnectRef <- newIORef []
1847− coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
1848− forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1096+ forM_ toAssign $ \(SomeAssignment h v) -> do
1849− writeIORef vRef v
1097+ writeIORef (holdValue h) v
1850− when debugInvalidate $ putStrLn $ "Invalidating Hold"
1851− writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1098+ writeIORef (holdInvalidators h) =<< invalidate toReconnectRef =<< readIORef (holdInvalidators h)
1852− mergeUpdates <- readIORef $ eventEnvMergeUpdates env
1099+ coincidenceInfos <- readIORef coincidenceInfosRef
1853− writeIORef (eventEnvMergeUpdates env) []
1100+ forM_ coincidenceInfos $ \(SomeCoincidenceInfo wsubInner subInner mcs) -> do
1854− when debugPropagate $ putStrLn "Updating merges"
1101+ touch subInner
1855− mergeSubscriptionsToKill <- runEventM $ concat <$> mapM _someMergeUpdate_update mergeUpdates
1102+ finalize wsubInner
1856− when debugPropagate $ putStrLn "Updating merges done"
1103+ mapM_ invalidateCoincidenceHeight mcs
1857 toReconnect <- readIORef toReconnectRef
1104 toReconnect <- readIORef toReconnectRef
1858− clearEventEnv env
1859− switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1105+ forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1860− oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1106+ wsub <- readIORef $ switchSubscribedSelfWeak subscribed
1107+ finalize wsub
1861 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1108 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1862− when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
1863 finalize wi
1109 finalize wi
1864− i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1110+ let !i = switchSubscribedOwnInvalidator subscribed
1865 wi' <- mkWeakPtrWithDebug i "wi'"
1111 wi' <- mkWeakPtrWithDebug i "wi'"
1866− writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1867 writeIORef (switchSubscribedBehaviorParents subscribed) []
1112 writeIORef (switchSubscribedBehaviorParents subscribed) []
1868− writeIORef (eventEnvHoldInits env) [] --TODO: Should we reuse this?
1869− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) $ eventEnvHoldInits env
1113+ e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed))
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
1871 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1114 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1872− sub <- newSubscriberSwitch subscribed
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'
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
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
1874 {-
1119 {-
1875− stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1120+ stackTrace <- liftIO $ liftM renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1876 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1121 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1877 EventRoot _ -> "EventRoot"
1122 EventRoot _ -> "EventRoot"
1878 EventNever -> "EventNever"
1123 EventNever -> "EventNever"
1879 _ -> "something else"
1124 _ -> "something else"
1880 -}
1125 -}
1881− writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1126+ writeIORef (switchSubscribedCurrentParent subscribed) subd'
1882− return oldSubscription
1127+ parentHeight <- readIORef $ eventSubscribedHeightRef subd'
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'
1888 myHeight <- readIORef $ switchSubscribedHeight subscribed
1128 myHeight <- readIORef $ switchSubscribedHeight subscribed
1889− when (parentHeight /= myHeight) $ do
1129+ if parentHeight == myHeight then return () else do
1890− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1130+ writeIORef (switchSubscribedHeight subscribed) parentHeight
1891− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1131+ mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
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
1132+ forM_ coincidenceInfos $ \(SomeCoincidenceInfo _ _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1897− mapM_ _someMergeUpdate_recalculateHeight mergeUpdates
1898 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1133 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1899− height <- calculateSwitchHeight subscribed
1134+ mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1900− updateSwitchHeight height subscribed
1901 return result
1135 return result
1902
1136
1903−newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1137+invalidHeight :: Int
1904−
1905−{-# INLINE zeroHeight #-}
1906−zeroHeight :: Height
1907−zeroHeight = Height 0
1908−
1909−{-# INLINE invalidHeight #-}
1138+invalidHeight = -1000
1910−invalidHeight :: Height
1139+
1911−invalidHeight = Height (-1000)
1140+invalidateSubscriberHeight :: WeakSubscriber a -> IO ()
1912−
1141+invalidateSubscriberHeight ws = do
1913−#ifdef DEBUG_CYCLES
1142+ ms <- deRefWeakSubscriber ws
1914−-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1143+ case ms of
1915−{-# INLINE invalidHeightBeingTraversed #-}
1144+ Nothing -> return () --TODO: cleanup?
1916−invalidHeightBeingTraversed :: Height
1145+ Just s -> case s of
1917−invalidHeightBeingTraversed = Height (-1001)
1146+ SubscriberPush _ subscribed -> do
1918−#endif
1147+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1919−
1148+ mapM_ invalidateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1920−{-# INLINE succHeight #-}
1149+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1921−succHeight :: Height -> Height
1150+ SubscriberMerge _ subscribed -> do
1922−succHeight h@(Height a) =
1151+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1923− if h == invalidHeight
1152+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1924− then invalidHeight
1153+ when (oldHeight /= invalidHeight) $ do
1925− else Height $ succ a
1154+ writeIORef (mergeSubscribedHeight subscribed) $ invalidHeight
1926−
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+
1927−invalidateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1179+invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1928invalidateCoincidenceHeight subscribed = do
1180invalidateCoincidenceHeight subscribed = do
1929 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1181 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1930 when (oldHeight /= invalidHeight) $ do
1182 when (oldHeight /= invalidHeight) $ do
1931− writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1183+ writeIORef (coincidenceSubscribedHeight subscribed) $ invalidHeight
1932− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1184+ mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
1933−
1185+
1934−updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
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
1935−updateSwitchHeight new subscribed = do
1187+
1936− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1188+recalculateSubscriberHeight :: WeakSubscriber a -> IO ()
1937− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
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
1938− when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1217+ when (oldHeight == invalidHeight) $ do
1218+ height <- calculateSwitchHeight subscribed
1219+ when (height /= invalidHeight) $ do
1939− writeIORef (switchSubscribedHeight subscribed) $! new
1220+ writeIORef (switchSubscribedHeight subscribed) height
1940− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1221+ mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1941−
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+
1942−recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1232+recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1943recalculateCoincidenceHeight subscribed = do
1233recalculateCoincidenceHeight subscribed = do
1944 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1234 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1945− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1235+ when (oldHeight == invalidHeight) $ do
1946 height <- calculateCoincidenceHeight subscribed
1236 height <- calculateCoincidenceHeight subscribed
1947 when (height /= invalidHeight) $ do
1237 when (height /= invalidHeight) $ do
1948− writeIORef (coincidenceSubscribedHeight subscribed) $! height
1238+ writeIORef (coincidenceSubscribedHeight subscribed) height
1949− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1239+ mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed) --TODO: This should probably be mandatory, just like with the merge and switch ones
1950
1240
1951−calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
1241+calculateMergeHeight :: MergeSubscribed k -> IO Int
1952−calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
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'
1953
1246
1247+calculateSwitchHeight :: SwitchSubscribed a -> IO Int
1248+calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef =<< readIORef (switchSubscribedCurrentParent subscribed)
1249+
1954−calculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO Height
1250+calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Int
1955calculateCoincidenceHeight subscribed = do
1251calculateCoincidenceHeight subscribed = do
1956− outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1252+ outerHeight <- readIORef $ eventSubscribedHeightRef $ coincidenceSubscribedOuterParent subscribed
1957− innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1253+ innerHeight <- maybe (return 0) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1958 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1254 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1959
1255
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+
1960−data SomeSwitchSubscribed x = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed x a)
1278+data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed (SwitchSubscribed a)
1279+
1280+debugInvalidate :: Bool
1281+debugInvalidate = False
1961
1282
1962−invalidate :: IORef [SomeSwitchSubscribed x] -> WeakList (Invalidator x) -> IO (WeakList (Invalidator x))
1283+invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1963invalidate toReconnectRef wis = do
1284invalidate toReconnectRef wis = do
1964 forM_ wis $ \wi -> do
1285 forM_ wis $ \wi -> do
1965 mi <- deRefWeak wi
1286 mi <- deRefWeak wi
1966 case mi of
1287 case mi of
1967 Nothing -> do
1288 Nothing -> do
1968− traceInvalidate "invalidate Dead"
1289+ when debugInvalidate $ liftIO $ putStrLn "invalidate Dead"
1969 return () --TODO: Should we clean this up here?
1290 return () --TODO: Should we clean this up here?
1970 Just i -> do
1291 Just i -> do
1971 finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1292 finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1972 case i of
1293 case i of
1973 InvalidatorPull p -> do
1294 InvalidatorPull p -> do
1974− traceInvalidate $ "invalidate: Pull" <> showNodeId p
1295+ when debugInvalidate $ liftIO $ putStrLn "invalidate Pull"
1975 mVal <- readIORef $ pullValue p
1296 mVal <- readIORef $ pullValue p
1976 forM_ mVal $ \val -> do
1297 forM_ mVal $ \val -> do
1977 writeIORef (pullValue p) Nothing
1298 writeIORef (pullValue p) Nothing
1978− writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1299+ writeIORef (pullSubscribedInvalidators val) =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1979 InvalidatorSwitch subscribed -> do
1300 InvalidatorSwitch subscribed -> do
1980− traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
1301+ when debugInvalidate $ liftIO $ putStrLn "invalidate Switch"
1981 modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1302 modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1982 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
1303 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
1983
1304
@@ -1985,172 +1306,90 @@ invalidate toReconnectRef wis = do
1985-- Reflex integration
1306-- Reflex integration
1986--------------------------------------------------------------------------------
1307--------------------------------------------------------------------------------
1987
1308
1988−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1309+data Spider
1989−-- When we're specializing, we include this from Reflex.Class instead, because
1310+
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
1311+instance R.Reflex Spider where
2041− {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2042− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
1312+ newtype Behavior Spider a = SpiderBehavior { unSpiderBehavior :: Behavior a }
2043− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
1313+ newtype Event Spider a = SpiderEvent { unSpiderEvent :: Event a }
2044− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
1314+ type PullM Spider = BehaviorM
2045− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
1315+ type PushM Spider = EventM
2046− type PullM (SpiderTimeline x) = SpiderPullM x
2047− type PushM (SpiderTimeline x) = SpiderPushM x
2048− {-# INLINABLE never #-}
1316+ {-# INLINE never #-}
2049− never = SpiderEvent eventNever
2050− {-# INLINABLE constant #-}
1317+ {-# INLINE constant #-}
2051− constant = SpiderBehavior . behaviorConst
1318+ never = SpiderEvent EventNever
2052− {-# INLINE push #-}
1319+ constant = SpiderBehavior . BehaviorConst
2053− push f = SpiderEvent . push (coerce f) . unSpiderEvent
2054− {-# INLINE pushCheap #-}
2055− pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
1320+ push f = SpiderEvent. push f . unSpiderEvent
2056− {-# INLINABLE pull #-}
2057− pull = SpiderBehavior . pull . coerce
1321+ pull = SpiderBehavior . pull
2058− {-# INLINABLE merge #-}
2059− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
1322+ merge = SpiderEvent . merge . (unsafeCoerce :: DMap k (R.Event Spider) -> DMap k Event)
2060− {-# INLINABLE fan #-}
2061 fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
1323 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
1324+ switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event Spider a) -> Behavior (Event a)) . unSpiderBehavior
2064− {-# INLINABLE coincidence #-}
2065− coincidence = SpiderEvent . coincidence . (coerce :: Event x (R.Event (SpiderTimeline x) a) -> Event x (Event x a)) . unSpiderEvent
1325+ coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event Spider a) -> Event (Event a)) . unSpiderEvent
2066− {-# INLINABLE current #-}
1326+
2067− current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
1327+instance R.MonadSample Spider SpiderHost where
2068− {-# INLINABLE updated #-}
1328+ {-# INLINE sample #-}
2069− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
1329+ sample = SpiderHost . readBehavior . unSpiderBehavior
2070− {-# INLINABLE unsafeBuildDynamic #-}
1330+
2071− unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
1331+instance R.MonadHold Spider SpiderHost where
2072− {-# INLINABLE unsafeBuildIncremental #-}
1332+ hold v0 = SpiderHost . liftM SpiderBehavior . runFrame . hold v0 . unSpiderEvent
2073− unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
1333+
2074− {-# INLINABLE mergeIncremental #-}
1334+instance R.MonadSample Spider BehaviorM where
2075− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
1335+ {-# INLINE sample #-}
2076− {-# INLINABLE mergeIncrementalWithMove #-}
1336+ sample = readBehaviorTracked . unSpiderBehavior
2077− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
1337+
2078− {-# INLINABLE currentIncremental #-}
1338+instance R.MonadSample Spider EventM where
2079− currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
1339+ {-# INLINE sample #-}
2080− {-# INLINABLE updatedIncremental #-}
1340+ sample = liftIO . readBehavior . unSpiderBehavior
2081− updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
1341+
2082− {-# INLINABLE incrementalToDynamic #-}
1342+instance R.MonadHold Spider EventM where
2083− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip push (dynamicUpdated i) $ \p -> do
1343+ {-# INLINE hold #-}
2084− c <- readBehaviorUntracked $ dynamicCurrent i
1344+ hold v0 e = SpiderBehavior <$> hold v0 (unSpiderEvent e)
2085− return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
1345+
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)
1346+data RootTrigger a = forall k. GCompare k => RootTrigger (IORef [WeakSubscriber a], IORef (DMap k Identity), k a)
2092−
2093−data SpiderEventHandle x a = SpiderEventHandle
1347+newtype SpiderEventHandle a = SpiderEventHandle { unEventHandle :: Event a }
2094− { spiderEventHandleSubscription :: EventSubscription x
1348+
2095− , spiderEventHandleValue :: IORef (Maybe a)
1349+instance R.MonadSubscribeEvent Spider SpiderHostFrame where
2096− }
1350+ subscribeEvent e = SpiderHostFrame $ do
2097−
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+
2098−instance MonadRef (EventM x) where
1363+instance MonadRef EventM where
2099− type Ref (EventM x) = Ref IO
1364+ type Ref EventM = Ref IO
2100− {-# INLINABLE newRef #-}
1365+ {-# INLINE newRef #-}
2101− {-# INLINABLE readRef #-}
1366+ {-# INLINE readRef #-}
2102− {-# INLINABLE writeRef #-}
1367+ {-# INLINE writeRef #-}
2103 newRef = liftIO . newRef
1368 newRef = liftIO . newRef
2104 readRef = liftIO . readRef
1369 readRef = liftIO . readRef
2105 writeRef r a = liftIO $ writeRef r a
1370 writeRef r a = liftIO $ writeRef r a
2106
1371
2107−instance MonadAtomicRef (EventM x) where
1372+instance MonadAtomicRef EventM where
2108− {-# INLINABLE atomicModifyRef #-}
1373+ {-# INLINE atomicModifyRef #-}
2109 atomicModifyRef r f = liftIO $ atomicModifyRef r f
1374 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2110
1375
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)
1376+newtype SpiderHost a = SpiderHost { runSpiderHost :: IO a } deriving (Functor, Applicative, Monad, 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
2135
1377
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)
2137
1379
2138−instance Monad (SpiderHostFrame x) where
1380+instance R.MonadSample Spider SpiderHostFrame where
2139− {-# INLINABLE (>>=) #-}
1381+ sample = SpiderHostFrame . R.sample --TODO: This can cause problems with laziness, so we should get rid of it if we can
2140− SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
1382+
2141− {-# INLINABLE (>>) #-}
2142− SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2143− {-# INLINABLE return #-}
2144− return x = SpiderHostFrame $ return x
1383+instance R.MonadHold Spider SpiderHostFrame where
2145− {-# INLINABLE fail #-}
1384+ {-# INLINE hold #-}
2146− fail s = SpiderHostFrame $ fail s
1385+ hold v0 e = SpiderHostFrame $ R.hold v0 e
2147
1386
2148−newEventWithTriggerIO :: (RootTrigger x a -> IO (IO ())) -> IO (Event x a)
1387+newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
2149newEventWithTriggerIO f = do
1388newEventWithTriggerIO f = do
2150 es <- newFanEventWithTriggerIO $ \Refl -> f
1389 es <- newFanEventWithTriggerIO $ \Refl -> f
2151 return $ select es Refl
1390 return $ select es Refl
2152
1391
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)
2154newFanEventWithTriggerIO f = do
1393newFanEventWithTriggerIO f = do
2155 occRef <- newIORef DMap.empty
1394 occRef <- newIORef DMap.empty
2156 subscribedRef <- newIORef DMap.empty
1395 subscribedRef <- newIORef DMap.empty
@@ -2159,28 +1398,46 @@ newFanEventWithTriggerIO f = do
2159 , rootSubscribed = subscribedRef
1398 , rootSubscribed = subscribedRef
2160 , rootInit = f
1399 , rootInit = f
2161 }
1400 }
2162− return $ EventSelector $ \k -> eventRoot k r
1401+ return $ EventSelector $ \k -> EventRoot k r
2163−
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+
2164−newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
1420+newtype ReadPhase a = ReadPhase { runReadPhase :: ResultM a } deriving (Functor, Applicative, Monad, MonadFix, R.MonadSample Spider, R.MonadHold Spider)
2165−
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+
2166−instance MonadRef (SpiderHost x) where
1427+instance MonadRef SpiderHost where
2167− type Ref (SpiderHost x) = Ref IO
1428+ type Ref SpiderHost = Ref IO
2168 newRef = SpiderHost . newRef
1429 newRef = SpiderHost . newRef
2169 readRef = SpiderHost . readRef
1430 readRef = SpiderHost . readRef
2170 writeRef r = SpiderHost . writeRef r
1431 writeRef r = SpiderHost . writeRef r
2171
1432
2172−instance MonadAtomicRef (SpiderHost x) where
1433+instance MonadAtomicRef SpiderHost where
2173 atomicModifyRef r = SpiderHost . atomicModifyRef r
1434 atomicModifyRef r = SpiderHost . atomicModifyRef r
2174
1435
2175−instance MonadRef (SpiderHostFrame x) where
1436+instance MonadRef SpiderHostFrame where
2176− type Ref (SpiderHostFrame x) = Ref IO
1437+ type Ref SpiderHostFrame = Ref IO
2177 newRef = SpiderHostFrame . newRef
1438 newRef = SpiderHostFrame . newRef
2178 readRef = SpiderHostFrame . readRef
1439 readRef = SpiderHostFrame . readRef
2179 writeRef r = SpiderHostFrame . writeRef r
1440 writeRef r = SpiderHostFrame . writeRef r
2180
1441
2181−instance MonadAtomicRef (SpiderHostFrame x) where
1442+instance MonadAtomicRef SpiderHostFrame where
2182 atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
1443 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 #-}
1+{-# LANGUAGE RecursiveDo, ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE RecursiveDo #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TupleSections #-}
10−{-# LANGUAGE TypeSynonymInstances #-}
11
2
12module Reflex.Bench.Focused where
3module Reflex.Bench.Focused where
13
4
14import Reflex
5import Reflex
15import Reflex.TestPlan
6import Reflex.TestPlan
16
7
17−import Control.Applicative
18−import Control.Monad
19import Control.Monad.Fix
8import Control.Monad.Fix
20−import Control.Monad.Identity
9+import Control.Monad
10+import Control.Applicative
11+import Data.Foldable
21−import Data.Traversable (for, traverse)
12+import Data.Traversable
22
13
23−import qualified Data.Dependent.Map as DMap
14+import Data.Maybe
24import Data.Map (Map)
15import Data.Map (Map)
25import qualified Data.Map as Map
16import qualified Data.Map as Map
26−import Data.Maybe
27−
28−import Data.Functor.Misc
29
17
18+import Data.Tuple
30import Data.List
19import Data.List
31import Data.List.Split
20import Data.List.Split
32−import Data.Tuple
33−
34−import Data.Monoid
35−import Data.Word
36−
37−import Control.DeepSeq
38−
39−import Prelude hiding (concat, sum)
40
21
22+import Prelude
41
23
42mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
24mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
43mergeTree n es | length es <= n = sum' es
25mergeTree n es | length es <= n = sum' es
@@ -47,17 +29,15 @@ mergeTree n es | length es <= n = sum' es
47 subTrees = map sum' merges
29 subTrees = map sum' merges
48 sum' = fmap sum . mconcat . fmap (fmap (:[]) )
30 sum' = fmap sum . mconcat . fmap (fmap (:[]) )
49
31
50−mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
32+mergeListDyn :: (Reflex t, MonadHold t m) => [Dynamic t a] -> m (Dynamic t [a])
51−mergeListDyn = mconcat . fmap (fmap pure)
33+mergeListDyn dyns = traverse (mapDyn pure) dyns >>= mconcatDyn
52
34
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)
54mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
36mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
55− | otherwise = mergeTreeDyn n $ fmap sumDyns merges
37+ | otherwise = traverse sumDyns merges >>= mergeTreeDyn n
56 where
38 where
57− merges = chunksOf n dyns
39+ merges = chunksOf n dyns
58− sumDyns = fmap sum . mergeListDyn
40+ sumDyns ds = mergeListDyn ds >>= mapDyn sum
59−
60−
61
41
62-- N events all firing for N frames
42-- N events all firing for N frames
63continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
43continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
@@ -70,13 +50,10 @@ occasional n period frames = traverse plan $ zipWith occs [1..] phases
70 phases = take (fromIntegral n) $ concat (repeat [1..period])
50 phases = take (fromIntegral n) $ concat (repeat [1..period])
71 occs i phase = (, i) <$> [1, phase+1..frames]
51 occs i phase = (, i) <$> [1, phase+1..frames]
72
52
73−
74−
75-- Event which never fires (but isn't 'never')
53-- Event which never fires (but isn't 'never')
76never' :: TestPlan t m => m (Event t Word)
54never' :: TestPlan t m => m (Event t Word)
77never' = plan []
55never' = plan []
78
56
79−
80-- Event which fires once on first frame
57-- Event which fires once on first frame
81event :: TestPlan t m => m (Event t Word)
58event :: TestPlan t m => m (Event t Word)
82event = plan [(1, 0)]
59event = plan [(1, 0)]
@@ -85,53 +62,30 @@ event = plan [(1, 0)]
85events :: TestPlan t m => Word -> m (Event t Word)
62events :: TestPlan t m => Word -> m (Event t Word)
86events n = plan $ (\i -> (i, i)) <$> [1..n]
63events n = plan $ (\i -> (i, i)) <$> [1..n]
87
64
88−
89-- N events all originating from one event
65-- N events all originating from one event
90fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
66fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
91fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
67fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
92
68
93−
94−-- Make a fan using the value in the event converting it to a singleton Map
95−-- use a fan of size n (key is the value mod n)
96−fanMerge :: Reflex t => Word -> Event t Word -> Event t Word
97−fanMerge n e = leftmost $ s <$> [0..n - 1] where
98− s k = select f (Const2 k)
99− f = fan (toMap <$> e)
100− toMap k = DMap.singleton (Const2 $ k `mod` n) (Identity k)
101−
102−-- Dumb version of the above using simple fmapMaybe to filter
103−fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
104−fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
105− s k = flip fmapMaybe e $ \j -> if k == j `mod` n
106− then Just k
107− else Nothing
108−
109−
110−
111-- Create an Event with a Map which is dense to size N
69-- Create an Event with a Map which is dense to size N
112denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
70denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
113denseMap n = plan [(1, m)] where
71denseMap n = plan [(1, m)] where
114 m = Map.fromList $ zip [1..n] [1..n]
72 m = Map.fromList $ zip [1..n] [1..n]
115
73
116−iter :: (a -> a) -> Word -> a -> a
74+fmapChain :: Reflex t => Word -> Event t Word -> (Event t Word)
117−iter _ 0 a = a
75+fmapChain 0 e = e
118−iter f n a = iter f (n - 1) (f a)
76+fmapChain n e = fmapChain (n - 1) (fmap (+1) e)
119−
120−iterM :: Monad m => (a -> m a) -> Word -> a -> m a
121−iterM _ 0 a = return a
122−iterM f n a = iterM f (n - 1) =<< f a
123−
124−fmapChain :: (Functor f) => Word -> f Word -> f Word
125−fmapChain = iter (fmap (+1))
126
77
127mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
78mapDynChain :: (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
129
81
130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
82joinDynChain :: (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
132
85
133combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
86combineDynChain :: (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
135
89
136pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
90pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
137pingPong e1 e2 = do
91pingPong e1 e2 = do
@@ -146,27 +100,27 @@ switchFactors i e = do
146 b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
100 b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
147 switchFactors (i - 1) (switch b)
101 switchFactors (i - 1) (switch b)
148
102
149−
150switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
103switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
151−switchChain = iterM (\e -> pingPong e ((+1) <$> e))
104+switchChain 0 e = return e
152−
105+switchChain i e = switchChain (i - 1) =<< pingPong e ((+1) <$> e)
153
106
154switchChain2 :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
107switchChain2 :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
108+switchChain2 0 e = return e
155−switchChain2 = iterM (\e -> pingPong e ((+1) <$> leftmost [e, e]))
109+switchChain2 i e = switchChain2 (i - 1) =<< pingPong e ((+1) <$> leftmost [e, e])
156
110
157switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
111switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
158−switchPromptlyChain = iterM $ \e ->
112+switchPromptlyChain 0 e = return e
113+switchPromptlyChain i e = do
114+ d <- holdDyn e (e <$ e)
159− switchPromptlyDyn <$> holdDyn e (e <$ e)
115+ switchPromptlyChain (i - 1) (switchPromptlyDyn d)
160−
161
116
162coinChain :: Reflex t => Word -> Event t Word -> Event t Word
117coinChain :: 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)
164
120
165−fanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
121+pullChain :: Reflex t => Word -> Behavior t Word -> Behavior t Word
166−fanMergeChain width = iter $ fanMerge width
122+pullChain 0 b = b
167−
123+pullChain n b = pullChain (n - 1) $ (+1) <$> b
168−fmapFanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
169−fmapFanMergeChain width = iter $ fmapFanMerge width
170
124
171-- | Data type for a collection (a map) which is updated by providing differences
125-- | Data type for a collection (a map) which is updated by providing differences
172data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
126data 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)))
175switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
129switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
176switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
130switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
177
131
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−
181switchMergeBehaviors :: 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))
132switchMergeBehaviors :: 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
183 where joinMap m = traverse sample =<< sample m
134 where joinMap m = traverse sample =<< sample m
184
135
185-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
136-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
186holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
137holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
187holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)) initial changes
138holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)) initial changes
188−
189 where
139 where
190 modify k Nothing items = Map.delete k items
140 modify k Nothing items = Map.delete k items
191 modify k (Just item) items = Map.insert k item items
141 modify k (Just item) items = Map.insert k item items
@@ -195,7 +145,6 @@ holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)
195holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
145holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
196holdMap = (current <$>) . holdMapDyn
146holdMap = (current <$>) . holdMapDyn
197
147
198−
199increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
148increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
200increasingMerge xs = do
149increasingMerge xs = do
201 e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
150 e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
@@ -218,43 +167,34 @@ modifyMerge xs = do
218 where
167 where
219 initial = Map.fromList $ zip [1..] xs
168 initial = Map.fromList $ zip [1..] xs
220
169
221−
222countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
170countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
223countMany = traverse (fmap current . count)
171countMany = traverse (fmap current . count)
224
172
225countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
173countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
226countManyDyn = traverse count
174countManyDyn = traverse count
227
175
228−
229-- Given a function which creates a sub-network (from a single event)
176-- Given a function which creates a sub-network (from a single event)
230-- Create a plan which uses a switch to substitute it
177-- Create a plan which uses a switch to substitute it
231switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
178switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
232switches numFrames f = do
179switches numFrames f = do
233 es <- events numFrames
180 es <- events numFrames
234 switch <$> hold never (makeEvents es)
181 switch <$> hold never (makeEvents es)
235−
236 where
182 where
237 makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
183 makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
238
184
239−
240−
241-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
185-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
242-- the other, which we're only interested in time for running frames
186-- the other, which we're only interested in time for running frames
243subscribing :: Word -> Word -> [(String, TestCase)]
187subscribing :: Word -> Word -> [(String, TestCase)]
244subscribing n frames =
188subscribing n frames =
245− [ testSub "fmapFanMerge" $ return . mergeList . fmapFan n
189+ [ testE "fmapFan merge" $ switches frames (return . mergeList . fmapFan n)
246− , testSub "fanMerge" $ return . fanMerge n
247− , testSub "fmapFan/mergeTree" $ return . mergeTree 8 . fmapFan n
190+ , testE "fmapFan/mergeTree" $ switches frames (return . mergeTree 8 . fmapFan n)
248− , testSub "fmapChain" $ return . fmapChain n
191+ , testE "fmapChain" $ switches frames (return . fmapChain n)
249− , testSub "switchChain" $ switchChain n
192+ , testE "switchChain" $ switches frames (switchChain n)
250− , testSub "switchPromptlyChain" $ switchPromptlyChain n
193+ , testE "switchPromptlyChain" $ switches frames (switchPromptlyChain n)
251− , testSub "switchFactors" $ switchFactors n
194+ , testE "switchFactors" $ switches frames (switchFactors n)
252− , testSub "coincidenceChain" $ return . coinChain n
195+ , testE "coincidenceChain" $ switches frames (return . coinChain n)
253 ]
196 ]
254
197
255− where
256− testSub :: (Eq a, Show a, NFData a) => String -> (forall t n. (Reflex t, MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> (String, TestCase)
257− testSub name test = testE name (switches frames test)
258
198
259-- Set of benchmark to test specifically merging dynamic collections
199-- Set of benchmark to test specifically merging dynamic collections
260-- a pattern which occurs frequently in reflex-dom collections
200-- a pattern which occurs frequently in reflex-dom collections
@@ -269,11 +209,6 @@ merging n =
269 , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
209 , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
270 , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
210 , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
271 , testB "modify behaviors" $ switchMergeBehaviors =<< modifyMerge =<< counters
211 , testB "modify behaviors" $ switchMergeBehaviors =<< modifyMerge =<< counters
272−
273−
274− -- , testE "increasing events/switchMerge" $ switchMergeMaybe =<< increasingMerge =<< sparse
275− -- , testE "decreasing events/switchMerge" $ switchMergeMaybe =<< decreasingMerge =<< sparse
276− -- , testE "modify events/switchMerge" $ switchMergeMaybe =<< modifyMerge =<< sparse
277 ]
212 ]
278
213
279 where
214 where
@@ -283,37 +218,15 @@ merging n =
283 counters :: TestPlan t m => m [Behavior t Int]
218 counters :: TestPlan t m => m [Behavior t Int]
284 counters = countMany =<< sparse
219 counters = countMany =<< sparse
285
220
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−
307dynamics :: Word -> [(String, TestCase)]
221dynamics :: Word -> [(String, TestCase)]
308dynamics n =
222dynamics n =
309 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
223 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
224 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
311 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
225 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312− , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
226+ , testE "dense mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< dense
313− , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
227+ , testE "sparse mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< sparse
314− , testE "mergeDyn" $ fmap (updated . fmap sum . mergeListDyn) sparse
228+ , testE "mergeDyn" $ fmap updated $ mapDyn sum =<< mergeListDyn =<< sparse
315 ] where
229 ] where
316−
317 d :: TestPlan t m => m (Dynamic t Word)
230 d :: TestPlan t m => m (Dynamic t Word)
318 d = count =<< events 10
231 d = count =<< events 10
319
232
@@ -332,25 +245,20 @@ firing n =
332
245
333 , testE "dense mergeTree" $ mergeTree 8 <$> dense
246 , testE "dense mergeTree" $ mergeTree 8 <$> dense
334 , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
247 , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
335−
336− -- Triggers test failure in Spider
337− --, testE "runFrame" $ events n
248+ , testE "runFrame" $ events n
338−
339 , testB "sum counters" $ do
249 , testB "sum counters" $ do
340 counts <- counters
250 counts <- counters
341 return $ pull $ sum <$> traverse sample counts
251 return $ pull $ sum <$> traverse sample counts
342
252
343− , testB "pullChain" $ fmapChain n . current <$> (count =<< events 4)
253+ , testB "pullChain" $ pullChain n . current <$> (count =<< events 4)
344 , testB "pullChain2" $ do
254 , testB "pullChain2" $ do
345− es <- events 4
255+ e' <- events 4
346− e' <- plan [(0, ())]
256+ b <- hold (constant 0) $ leftmost
347−
257+ [ constant 0 <$ ffilter (==4) e'
348− b <- hold (constant 0) $
349− pushAlways (const $ fmapChain n <$> hold 0 es) e'
258+ , pushAlways (const $ pullChain n <$> hold 0 e') e'
350−
259+ ]
351 return (join b)
260 return (join b)
352 , testB "counters/pullTree" $ mergeTree 8 <$> counters
261 , testB "counters/pullTree" $ mergeTree 8 <$> counters
353−
354 ]
262 ]
355 where
263 where
356 e :: TestPlan t m => m (Event t Word)
264 e :: TestPlan t m => m (Event t Word)
@@ -363,7 +271,3 @@ firing n =
363 counters :: TestPlan t m => m [Behavior t Int]
271 counters :: TestPlan t m => m [Behavior t Int]
364 counters = countMany =<< sparse
272 counters = countMany =<< sparse
365
273
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 #-}
1+{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
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 #-}
11module Reflex.Plan.Reflex
2module Reflex.Plan.Reflex
12 ( TestPlan(..)
3 ( TestPlan(..)
13 , runPlan
4 , runPlan
14− , Plan (..)
5+ , Plan
15 , Schedule
6 , Schedule
16 , Firing (..)
7 , Firing (..)
17 , MonadIORef
8 , MonadIORef
18
9
19
10
20 , readSchedule
11 , readSchedule
21− , readSchedule_
22 , testSchedule
12 , testSchedule
23 , readEvent'
13 , readEvent'
24 , makeDense
14 , makeDense
@@ -37,15 +27,13 @@ import Control.Monad
37import Control.Monad.Identity
27import Control.Monad.Identity
38import Control.Monad.State.Strict
28import Control.Monad.State.Strict
39
29
40−import Control.Monad.Ref
41import Data.Dependent.Sum (DSum (..))
30import Data.Dependent.Sum (DSum (..))
42−import qualified Data.IntMap.Strict as IntMap
43−import Data.Maybe
44import Data.Monoid
31import 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+
46
36
47−import Control.DeepSeq
48−import Control.Exception
49import Data.IntMap.Strict (IntMap)
37import Data.IntMap.Strict (IntMap)
50import Data.IORef
38import Data.IORef
51import System.Mem
39import System.Mem
@@ -87,7 +75,7 @@ instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => T
87firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
75firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
88firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
76firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
89
77
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)
91runPlan (Plan p) = runHostFrame $ runStateT p mempty
79runPlan (Plan p) = runHostFrame $ runStateT p mempty
92
80
93
81
@@ -102,31 +90,29 @@ makeDense s = fromMaybe (emptyRange 0) $ do
102-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
90-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
103-- after the last event)
91-- after the last event)
104-- performGC is called at each frame to test for GC issues
92-- 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)
106testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
94testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
107
95
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)
109readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
97readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
110
98
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
115triggerFrame readResult _ occs = do
100triggerFrame readResult _ occs = do
116− triggers <- catMaybes <$> traverse firingTrigger occs
101+ triggers <- {-# SCC "catMaybes/traverse" #-} catMaybes <$> traverse firingTrigger occs
117− liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
102+ {-# SCC fireEventsAndRead #-} fireEventsAndRead triggers readResult
103+
118
104
119readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
105readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
120−readEvent' = readEvent >=> sequenceA
106+readEvent' = readEvent >=> sequence
121
107
122
108
123-- Convenience functions for running tests producing Events/Behaviors
109-- 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)
125runTestB p = do
111runTestB p = do
126 (b, s) <- runPlan p
112 (b, s) <- runPlan p
127 testSchedule s $ sample b
113 testSchedule s $ sample b
128
114
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))
130runTestE p = do
116runTestE p = do
131 (e, s) <- runPlan p
117 (e, s) <- runPlan p
132 h <- subscribeEvent e
118 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 #-}
1+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, BangPatterns, 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 #-}
15module Reflex.Test.CrossImpl (test) where
2module Reflex.Test.CrossImpl (test) where
16
3
17−import Prelude hiding (and, foldl, mapM, mapM_, sequence, sequence_)
4+import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl, and)
18
5
19−import Control.Monad.Ref
20import Reflex.Class
6import Reflex.Class
21−import Reflex.Dynamic
22import Reflex.Host.Class
7import Reflex.Host.Class
23−import qualified Reflex.Pure as P
8+import Reflex.Dynamic
24import qualified Reflex.Spider.Internal as S
9import qualified Reflex.Spider.Internal as S
10+import qualified Reflex.Pure as P
11+import Control.Monad.Ref
25
12
26−import Control.Arrow (second, (&&&))
27−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
13+import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
28−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
14+import qualified Data.Set as Set
29−import Control.Monad.Writer hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
30−import Data.Dependent.Map (DSum (..))
31−import Data.Foldable
32import Data.Map.Strict (Map)
15import Data.Map.Strict (Map)
33import qualified Data.Map.Strict as Map
16import qualified Data.Map.Strict as Map
34−import qualified Data.Set as Set
17+import Control.Arrow (second, (&&&))
35import Data.Traversable
18import 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 (..))
37import System.Mem
23import System.Mem
24+import System.Exit
38
25
39import System.IO.Unsafe
26import System.IO.Unsafe
40
27
@@ -86,7 +73,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
86 outputs <- forM times $ \t -> do
73 outputs <- forM times $ \t -> do
87 forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
74 forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
88 bOutput <- sample b'
75 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
90 mret <- readRef reTrigger
77 mret <- readRef reTrigger
91 let firing = case mret of
78 let firing = case mret of
92 Just ret -> [ret :=> Identity val]
79 Just ret -> [ret :=> Identity val]
@@ -94,7 +81,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
94 fireEventsAndRead firing $ sequence =<< readEvent e'Handle
81 fireEventsAndRead firing $ sequence =<< readEvent e'Handle
95 liftIO performGC
82 liftIO performGC
96 return (t, (bOutput, eOutput))
83 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)
98
85
99tracePerf :: Show a => a -> b -> b
86tracePerf :: Show a => a -> b -> b
100tracePerf = flip const
87tracePerf = flip const
@@ -124,7 +111,7 @@ testCases =
124 b' <- hold "123" e
111 b' <- hold "123" e
125 return (b', e)
112 return (b', e)
126 , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
113 , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
127− e' <- updated <$> count e
114+ e' <- liftM updated $ count e
128 b' <- hold (0 :: Int) e'
115 b' <- hold (0 :: Int) e'
129 return (b', e')
116 return (b', e')
130 , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
117 , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -137,19 +124,23 @@ testCases =
137 return (b, e'')
124 return (b, e'')
138 , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
125 , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
139 let e' = flip pushAlways e $ const $ do
126 let e' = flip pushAlways e $ const $ do
140− let eab = splitRecombineEvent e
127+ let ea = fmap (const "a") e
128+ let eb = fmap (const "b") e
129+ let eab = leftmost [ea, eb]
141− switch <$> hold eab never
130+ liftM switch $ hold eab never
142 e'' = coincidence e'
131 e'' = coincidence e'
143 return (b, e'')
132 return (b, e'')
144 , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
133 , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
145 let e' = flip pushAlways e $ const $ do
134 let e' = flip pushAlways e $ const $ do
146− let eab = splitRecombineEvent e
135+ let ea = fmap (const "a") e
136+ let eb = fmap (const "b") e
137+ let eab = leftmost [ea, eb]
147− switch <$> hold eab (fmap (const e) e)
138+ liftM switch $ hold eab (fmap (const e) e)
148 e'' = coincidence e'
139 e'' = coincidence e'
149 return (b, e'')
140 return (b, e'')
150 , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
141 , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
151 let e' = leftmost [e, e]
142 let e' = leftmost [e, e]
152− e'' <- switch <$> hold e' (fmap (const e) e)
143+ e'' <- liftM switch $ hold e' (fmap (const e) e)
153 return (b, e'')
144 return (b, e'')
154 , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
145 , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
155 let e' = fmap (const e) e
146 let e' = fmap (const e) e
@@ -169,11 +160,11 @@ testCases =
169 return (b, e'')
160 return (b, e'')
170 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
161 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
171 let e' = leftmost [e, e]
162 let e' = leftmost [e, e]
172− e'' <- switch <$> hold never (fmap (const e') e)
163+ e'' <- liftM switch $ hold never (fmap (const e') e)
173 return (b, e'')
164 return (b, e'')
174 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
165 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
175 let e' = flip push e $ \_ -> do
166 let e' = flip push e $ \_ -> do
176− Just <$> onceE e
167+ return . Just =<< onceE e
177 e'' <- switchPromptly never e'
168 e'' <- switchPromptly never e'
178 return (b, e'')
169 return (b, e'')
179 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
170 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -182,7 +173,7 @@ testCases =
182 e'' <- switchPromptly never e'
173 e'' <- switchPromptly never e'
183 return (b, e'')
174 return (b, e'')
184 , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
175 , (,) "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
186 e'' = coincidence e'
177 e'' = coincidence e'
187 return (b, e'')
178 return (b, e'')
188 , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
179 , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -221,19 +212,12 @@ testCases =
221 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
212 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222 bb <- hold "b" e
213 bb <- hold "b" e
223 bd <- hold never . fmap (const e) =<< onceE e
214 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
225 let eInner = switch bd
216 let eInner = switch bd
226 e' = leftmost [eOuter, eInner]
217 e' = leftmost [eOuter, eInner]
227 return (b, e')
218 return (b, e')
228 ]
219 ]
229
220
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−
237test :: IO ()
221test :: IO ()
238test = do
222test = do
239 results <- forM testCases $ \(name, TestCase inputs builder) -> do
223 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 #-}
1+{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE UndecidableInstances #-}
9module Reflex.TestPlan
2module Reflex.TestPlan
10 ( TestPlan(..)
3 ( TestPlan(..)
11
4
@@ -17,11 +10,11 @@ module Reflex.TestPlan
17
10
18 ) where
11 ) where
19
12
20−import Control.DeepSeq
21−import Control.Monad.Fix
22−import Data.Word
23import Reflex.Class
13import Reflex.Class
14+import Control.Monad.Fix
24
15
16+import qualified Data.IntMap as IntMap
17+import Data.IntMap
25
18
26import Prelude
19import Prelude
27
20
@@ -40,13 +33,13 @@ type TestE a = forall t m. TestPlan t m => m (Event t a)
40type TestB a = forall t m. TestPlan t m => m (Behavior t a)
33type TestB a = forall t m. TestPlan t m => m (Behavior t a)
41
34
42data TestCase where
35data TestCase where
43− TestE :: (Show a, Eq a, NFData a) => TestE a -> TestCase
36+ TestE :: (Show a, Eq a) => TestE a -> TestCase
44− TestB :: (Show a, Eq a, NFData a) => TestB a -> TestCase
37+ TestB :: (Show a, Eq a) => TestB a -> TestCase
45
38
46-- Helpers to declare test cases
39-- 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)
48testE name test = (name, TestE test)
41testE name test = (name, TestE test)
49
42
50−testB :: (Eq a, Show a, NFData a) => String -> TestB a -> (String, TestCase)
43+testB :: (Eq a, Show a) => String -> TestB a -> (String, TestCase)
51testB name test = (name, TestB test)
44testB name test = (name, TestB test)
52
45
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−