on develop help sign in
Merge remote-tracking branch 'origin/wip-optimizer' into develop

# Conflicts: # .gitignore # src/Reflex/Class.hs # src/Reflex/Spider/Internal.hs # test/Reflex/Test/Micro.hs

commit3835a1ff6d133d7c02e11ed7cfae8593f945e448 authorRyan Trinkle <ryan.trinkle@gmail.com> dateWed 9 Nov 2016, 11:39 (10 years ago) parent64902ea1ff7dbf
25 files changed+2055−1600
.gitignore+3 bench/RunAll.hs+1−1 default.nix+13−13 reflex.cabal+25−10 src/Data/Functor/Misc.hs+56 src/Data/WeakBag.hs+22−24 src/Reflex/Class.hs+183−132 src/Reflex/Deletable/Class.hs−17 src/Reflex/Dynamic.hs+2−1 src/Reflex/Dynamic/TH.hs+1 src/Reflex/Dynamic/Uniq.hs+1 src/Reflex/DynamicWriter.hs+69−52 src/Reflex/FunctorMaybe.hs+25 src/Reflex/Host/Class.hs+13 src/Reflex/Optimizer.hs+40 src/Reflex/Patch.hs+92 src/Reflex/PerformEvent/Base.hs+227−78 src/Reflex/PerformEvent/Class.hs+47 src/Reflex/PostBuild/Class.hs+25−5 src/Reflex/Pure.hs+7−2 src/Reflex/Spider.hs+4−1 src/Reflex/Spider/Internal.hs+1000−1260 src/Reflex/Spider/SpiderTimeline.include.hs+121 src/Reflex/Spider/SpiderTimelineHost.include.hs+57 test/Reflex/Test/Micro.hs+21−4
.gitignore+3
@@ -1,6 +1,7 @@
11dist
22cabal.sandbox.config
33.cabal-sandbox/
4+dist-*
45cabal-dev
56*.o
67*.hi
@@ -33,3 +34,5 @@ hsenv.log
3334.#*
3435/shell.nix
3536/ghci-tmp
37+*.dump-*
38+*.verbose-core2core
bench/RunAll.hs+1−1
@@ -74,7 +74,7 @@ newtype Ignore a = Ignore a
7474instance NFData (Ignore a) where
7575 rnf !_ = ()
7676
77−instance NFData (SpiderEventHandle a) where
77+instance NFData (SpiderEventHandle x a) where
7878 rnf !_ = ()
7979
8080instance NFData (Behavior t a) where
default.nix+13−13
@@ -1,24 +1,24 @@
1−{ mkDerivation, base, bifunctors, containers, deepseq
2−, dependent-map, dependent-sum, exception-transformers
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
4−, monad-control, mtl , primitive, ref-tf, semigroupoids
5−, semigroups, split, stdenv, stm , syb, template-haskell
6−, these, transformers, transformers-compat
1+{ mkDerivation, base, bifunctors, containers, data-default, deepseq
2+, dependent-map, dependent-sum, exception-transformers, ghc
3+, haskell-src-exts, haskell-src-meta, lens, MemoTrie, monad-control
4+, mtl, prim-uniq, primitive, ref-tf, semigroupoids, semigroups
5+, split, stdenv, stm, syb, template-haskell, these, transformers
6+, transformers-compat
77}:
88mkDerivation {
99 pname = "reflex";
10− version = "0.4.0";
10+ version = "0.5.0";
1111 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
1212 libraryHaskellDepends = [
13− base bifunctors containers dependent-map dependent-sum
14− exception-transformers haskell-src-exts haskell-src-meta lens
15− MemoTrie monad-control mtl primitive ref-tf semigroupoids
16− semigroups stm syb template-haskell these transformers
13+ base bifunctors containers data-default dependent-map dependent-sum
14+ exception-transformers ghc haskell-src-exts haskell-src-meta lens
15+ MemoTrie monad-control mtl prim-uniq primitive ref-tf semigroupoids
16+ semigroups stm syb template-haskell these transformers
1717 transformers-compat
1818 ];
1919 testHaskellDepends = [
20− base bifunctors containers deepseq dependent-map dependent-sum
21− hlint mtl ref-tf split transformers
20+ base bifunctors containers deepseq dependent-map dependent-sum mtl
21+ ref-tf split transformers
2222 ];
2323 homepage = "https://github.com/reflex-frp/reflex";
2424 description = "Higher-order Functional Reactive Programming";
reflex.cabal+25−10
@@ -13,6 +13,11 @@ Cabal-version: >=1.9.2
1313homepage: https://github.com/reflex-frp/reflex
1414bug-reports: https://github.com/reflex-frp/reflex/issues
1515
16+flag specialize-to-spidertimeline-global
17+ description: Specialize all Reflex functions to the SpiderTimeline Global implementation. This may improve performance, but will disable all other implementations.
18+ default: False
19+ manual: True
20+
1621library
1722 hs-source-dirs: src
1823 build-depends:
@@ -24,11 +29,13 @@ library
2429 dependent-map >= 0.2.2 && < 0.3,
2530 dependent-sum == 0.3.*,
2631 exception-transformers == 0.4.*,
32+ ghc,
2733 haskell-src-exts >= 1.16 && < 1.18,
2834 haskell-src-meta == 0.6.*,
2935 lens >= 4.7 && < 5,
3036 monad-control >= 1.0.1 && < 1.1,
3137 mtl >= 2.1 && < 2.3,
38+ prim-uniq >= 0.1.0.1 && < 0.2,
3239 primitive >= 0.5 && < 0.7,
3340 ref-tf == 0.4.*,
3441 semigroupoids >= 4.0 && < 6,
@@ -45,16 +52,17 @@ library
4552 Data.WeakBag,
4653 Reflex,
4754 Reflex.Class,
48− Reflex.Deletable.Class,
4955 Reflex.Dynamic,
5056 Reflex.Dynamic.TH,
5157 Reflex.Dynamic.Uniq,
5258 Reflex.DynamicWriter,
59+ Reflex.FunctorMaybe,
5360 Reflex.Host.Class,
61+ Reflex.Optimizer,
62+ Reflex.Patch,
5463 Reflex.PerformEvent.Base,
5564 Reflex.PerformEvent.Class,
5665 Reflex.PostBuild.Class,
57− Reflex.Pure,
5866 Reflex.Spider,
5967 Reflex.Spider.Internal
6068
@@ -62,6 +70,11 @@ library
6270 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
6371 ghc-prof-options: -auto-all
6472
73+ if flag(specialize-to-spidertimeline-global)
74+ cpp-options: -DSPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
75+ else
76+ exposed-modules: Reflex.Pure
77+
6578test-suite semantics
6679 type: exitcode-stdio-1.0
6780 main-is: semantics.hs
@@ -79,15 +92,17 @@ test-suite semantics
7992 reflex,
8093 split,
8194 transformers >= 0.3
82−
83−test-suite hlint
84− type: exitcode-stdio-1.0
85− main-is: hlint.hs
86− hs-source-dirs: test
87− build-depends: base, hlint == 1.9.*
88− if impl(ghcjs)
95+ if flag(specialize-to-spidertimeline-global)
8996 buildable: False
9097
98+--test-suite hlint
99+-- type: exitcode-stdio-1.0
100+-- main-is: hlint.hs
101+-- hs-source-dirs: test
102+-- build-depends: base, hlint == 1.9.*
103+-- if impl(ghcjs)
104+-- buildable: False
105+
91106benchmark spider-bench
92107 type: exitcode-stdio-1.0
93108 hs-source-dirs: bench
@@ -123,7 +138,7 @@ benchmark saulzar-bench
123138 mtl,
124139 primitive,
125140 process,
126− ref-tf == 0.4,
141+ ref-tf,
127142 reflex,
128143 split,
129144 stm,
src/Data/Functor/Misc.hs+56
@@ -3,11 +3,13 @@
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE GADTs #-}
55{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE LambdaCase #-}
67{-# LANGUAGE MultiParamTypeClasses #-}
78{-# LANGUAGE PolyKinds #-}
89{-# LANGUAGE RankNTypes #-}
910{-# LANGUAGE ScopedTypeVariables #-}
1011{-# LANGUAGE StandaloneDeriving #-}
12+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1113-- | This module provides types and functions with no particular theme, but
1214-- which are relevant to the use of 'Functor'-based datastructures like
1315-- 'Data.Dependent.Map.DMap'.
@@ -20,7 +22,12 @@ module Data.Functor.Misc
2022 , WrapArg (..)
2123 -- * Convenience functions for DMap
2224 , mapWithFunctorToDMap
25+ , mapKeyValuePairsMonotonic
2326 , combineDMapsWithKey
27+ , EitherTag (..)
28+ , dmapToThese
29+ , eitherToDSum
30+ , dsumToEither
2431 -- * Deprecated functions
2532 , sequenceDmap
2633 , wrapDMap
@@ -132,6 +139,55 @@ combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList m
132139 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
133140 GGT -> (hk :=> f hk (That hv)) : go gs hs'
134141
142+-- | Extract the values of a 'DMap' of 'EitherTag's.
143+dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
144+dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
145+ (Nothing, Nothing) -> Nothing
146+ (Just (Identity a), Nothing) -> Just $ This a
147+ (Nothing, Just (Identity b)) -> Just $ That b
148+ (Just (Identity a), Just (Identity b)) -> Just $ These a b
149+
150+-- | Tag type for 'Either' to use it as a 'DSum'.
151+data EitherTag l r a where
152+ LeftTag :: EitherTag l r l
153+ RightTag :: EitherTag l r r
154+
155+instance GEq (EitherTag l r) where
156+ geq a b = case (a, b) of
157+ (LeftTag, LeftTag) -> Just Refl
158+ (RightTag, RightTag) -> Just Refl
159+ _ -> Nothing
160+
161+instance GCompare (EitherTag l r) where
162+ gcompare a b = case (a, b) of
163+ (LeftTag, LeftTag) -> GEQ
164+ (LeftTag, RightTag) -> GLT
165+ (RightTag, LeftTag) -> GGT
166+ (RightTag, RightTag) -> GEQ
167+
168+instance GShow (EitherTag l r) where
169+ gshowsPrec _ a = case a of
170+ LeftTag -> showString "LeftTag"
171+ RightTag -> showString "RightTag"
172+
173+instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
174+ showTaggedPrec t n (Identity a) = case t of
175+ LeftTag -> showsPrec n a
176+ RightTag -> showsPrec n a
177+
178+-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
179+eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
180+eitherToDSum = \case
181+ Left a -> (LeftTag :=> Identity a)
182+ Right b -> (RightTag :=> Identity b)
183+
184+-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
185+dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
186+dsumToEither = \case
187+ (LeftTag :=> Identity a) -> Left a
188+ (RightTag :=> Identity b) -> Right b
189+
190+
135191--------------------------------------------------------------------------------
136192-- Deprecated functions
137193--------------------------------------------------------------------------------
src/Data/WeakBag.hs+22−24
@@ -1,3 +1,6 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE ExistentialQuantification #-}
3+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
14-- | This module defines the 'WeakBag' type, which represents a mutable
25-- collection of items that does not cause the items to be retained in memory.
36-- This is useful for situations where a value needs to be inspected or modified
@@ -12,7 +15,6 @@ module Data.WeakBag
1215 , remove
1316 ) where
1417
15−import Control.Concurrent.STM
1618import Control.Exception
1719import Control.Monad hiding (forM_, mapM_)
1820import Control.Monad.IO.Class
@@ -28,15 +30,15 @@ import Prelude hiding (mapM_, traverse)
2830-- that is, they can still be garbage-collected. As long as the @a@s remain
2931-- alive, the 'WeakBag' will continue to refer to them.
3032data WeakBag a = WeakBag
31− { _weakBag_nextId :: {-# UNPACK #-} !(TVar Int) --TODO: what if this wraps around?
32− , _weakBag_children :: {-# UNPACK #-} !(TVar (IntMap (Weak a)))
33+ { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
34+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
3335 }
3436
3537-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
3638-- the caller retains the ticket, the item is guranteed to stay in memory (and
3739-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
3840-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
39−data WeakBagTicket a = WeakBagTicket
41+data WeakBagTicket = forall a. WeakBagTicket
4042 { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
4143 , _weakBagTicket_item :: {-# NOUNPACK #-} !a
4244 }
@@ -50,30 +52,24 @@ insert :: a -- ^ The item
5052 -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
5153 -- (whether automatically by the item being garbage
5254 -- collected or manually via 'remove')
53− -> IO (WeakBagTicket a) -- ^ Returns a 'WeakBagTicket' that ensures the
54− -- item is retained and allows the item to be
55− -- removed.
55+ -> IO WeakBagTicket -- ^ Returns a 'WeakBagTicket' that ensures the item
56+ -- is retained and allows the item to be removed.
5657insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
5758 a' <- evaluate a
5859 wbRef' <- evaluate wbRef
59− myId <- atomically $ do
60− myId <- readTVar nextId
61− writeTVar nextId $! succ myId
62− return myId
60+ myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
6361 let cleanup = do
6462 wb <- readIORef wbRef'
6563 mb <- deRefWeak wb
6664 forM_ mb $ \b -> do
67− isLastNode <- atomically $ do --TODO: Should this run even when mb is Nothing?
68− cs <- readTVar children
69− let csWithoutMe = IntMap.delete myId cs
70− writeTVar children $! csWithoutMe
71− return $ IntMap.size csWithoutMe == 0
72− when isLastNode $ finalizer b
65+ csWithoutMe <- atomicModifyIORef children $ \cs ->
66+ let !csWithoutMe = IntMap.delete myId cs
67+ in (csWithoutMe, csWithoutMe)
68+ when (IntMap.null csWithoutMe) $ finalizer b
7369 return ()
7470 return ()
7571 wa <- mkWeakPtr a' $ Just cleanup
76− atomically $ modifyTVar' children $ IntMap.insert myId wa
72+ atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
7773 return $ WeakBagTicket
7874 { _weakBagTicket_weakItem = wa
7975 , _weakBagTicket_item = a'
@@ -83,8 +79,8 @@ insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
8379{-# INLINE empty #-}
8480empty :: IO (WeakBag a)
8581empty = {-# SCC "empty" #-} do
86− nextId <- newTVarIO 1
87− children <- newTVarIO IntMap.empty
82+ nextId <- newIORef 1
83+ children <- newIORef IntMap.empty
8884 let bag = WeakBag
8985 { _weakBag_nextId = nextId
9086 , _weakBag_children = children
@@ -94,7 +90,7 @@ empty = {-# SCC "empty" #-} do
9490-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
9591-- 'empty', then using 'insert'.
9692{-# INLINE singleton #-}
97−singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket a)
93+singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket)
9894singleton a wbRef finalizer = {-# SCC "singleton" #-} do
9995 bag <- empty
10096 ticket <- insert a bag wbRef finalizer
@@ -107,7 +103,7 @@ singleton a wbRef finalizer = {-# SCC "singleton" #-} do
107103-- is made about the order of the traversal.
108104traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
109105traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
110− cs <- liftIO $ readTVarIO children
106+ cs <- liftIO $ readIORef children
111107 forM_ cs $ \c -> do
112108 ma <- liftIO $ deRefWeak c
113109 mapM_ f ma
@@ -115,6 +111,8 @@ traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
115111-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
116112-- on the same 'WeakBagTicket'.
117113{-# INLINE remove #-}
118−remove :: WeakBagTicket a -> IO ()
119−remove = {-# SCC "remove" #-} finalize . _weakBagTicket_weakItem
114+remove :: WeakBagTicket -> IO ()
115+remove (WeakBagTicket w _) = {-# SCC "remove" #-} finalize w
120116--TODO: Should 'remove' also drop the reference to the item?
117+
118+--TODO: can/should we provide a null WeakBagTicket?
src/Reflex/Class.hs+183−132
@@ -1,4 +1,6 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveFunctor #-}
3+{-# LANGUAGE EmptyDataDecls #-}
24{-# LANGUAGE FlexibleContexts #-}
35{-# LANGUAGE FlexibleInstances #-}
46{-# LANGUAGE FunctionalDependencies #-}
@@ -8,24 +10,32 @@
810{-# LANGUAGE NoMonomorphismRestriction #-}
911{-# LANGUAGE RankNTypes #-}
1012{-# LANGUAGE RecursiveDo #-}
13+{-# LANGUAGE RoleAnnotations #-}
1114{-# LANGUAGE ScopedTypeVariables #-}
15+{-# LANGUAGE StandaloneDeriving #-}
1216{-# LANGUAGE TypeFamilies #-}
1317{-# LANGUAGE TypeOperators #-}
1418{-# LANGUAGE UndecidableInstances #-}
19+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
21+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
22+#endif
1523
1624-- | This module contains the Reflex interface, as well as a variety of
1725-- convenience functions for working with 'Event's, 'Behavior's, and other
1826-- signals.
1927module Reflex.Class
20− ( -- * Primitives
21− Reflex (..)
28+ ( module Reflex.Patch
29+ -- * Primitives
30+ , Reflex (..)
31+ , coerceBehavior
32+ , coerceEvent
33+ , coerceDynamic
2234 , MonadSample (..)
2335 , MonadHold (..)
2436 -- ** 'fan'-related types
2537 , EventSelector (..)
2638 -- ** 'Incremental'-related types
27− , Patch (..)
28− , PatchDMap (..)
2939 -- * Convenience functions
3040 , constDyn
3141 , pushAlways
@@ -81,10 +91,40 @@ module Reflex.Class
8191 , ffilter
8292 -- * Miscellaneous convenience functions
8393 , ffor
94+ , MonadAdjust (..)
8495 -- * Deprecated functions
8596 , appendEvents
8697 , onceE
8798 , sequenceThese
99+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
100+ , Spider
101+ , SpiderEnv
102+ , SpiderTimeline
103+ , Global
104+ , Behavior (..)
105+ , Event (..)
106+ , Dynamic (..)
107+ , Incremental (..)
108+ , never
109+ , constant
110+ , push
111+ , pull
112+ , merge
113+ , fan
114+ , switch
115+ , coincidence
116+ , current
117+ , updated
118+ , unsafeBuildDynamic
119+ , unsafeBuildIncremental
120+ , mergeIncremental
121+ , currentIncremental
122+ , updatedIncremental
123+ , incrementalToDynamic
124+ , behaviorCoercion
125+ , eventCoercion
126+ , dynamicCoercion
127+#endif
88128 ) where
89129
90130import Control.Applicative
@@ -97,34 +137,44 @@ import Control.Monad.Trans.RWS (RWST ())
97137import Control.Monad.Trans.Writer (WriterT ())
98138import Data.Align
99139import Data.Bifunctor
140+import Data.Coerce
100141import Data.Default
101−import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
142+import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
102143import qualified Data.Dependent.Map as DMap
103−import Data.Dependent.Sum (ShowTag (..))
104144import Data.Either
105145import Data.Foldable
106146import Data.Functor.Bind hiding (join)
107147import qualified Data.Functor.Bind as Bind
108−import Data.Functor.Compose
109−import Data.Functor.Constant
110148import Data.Functor.Misc
111149import Data.Functor.Plus
112−import Data.GADT.Compare ((:~:) (..), GEq (..))
113−import Data.GADT.Show (GShow (..))
114150import Data.List.NonEmpty (NonEmpty (..))
115151import Data.Map (Map)
116152import Data.Maybe
117153import Data.Monoid hiding (Alt, (<>))
118−import Data.Semigroup (Semigroup, sconcat, stimes, stimesIdempotentMonoid, (<>))
154+import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
119155import Data.String
120156import Data.These
121157import Data.Traversable
158+import Data.Type.Coercion
159+import Reflex.Patch
160+import Reflex.FunctorMaybe
161+
162+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
163+import Reflex.Spider.Internal (Global, SpiderPullM, SpiderPushM (..), HasSpiderTimeline, EventM, readBehaviorUntracked, readBehaviorTracked, dynamicHoldIdentity, dynamicHold, dynamicDynIdentity, newMapDyn, dynamicCurrent, dynamicUpdated, unsafeDyn, dynamicConst, behaviorHoldIdentity, SpiderHost (..), runFrame, runSpiderHostFrame, SpiderHostFrame (..))
164+import qualified Reflex.Spider.Internal
165+import qualified Reflex.Spider.Internal as S
166+import Unsafe.Coerce
167+#endif
122168
123169-- Note: must come last to silence warnings due to AMP on GHC < 7.10
124170import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
125171
126172import Debug.Trace (trace)
127173
174+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
175+#include "Spider/SpiderTimeline.include.hs"
176+#endif
177+
128178-- | The 'Reflex' class contains all the primitive functionality needed for
129179-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
130180-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
@@ -133,11 +183,12 @@ import Debug.Trace (trace)
133183class ( MonadHold t (PushM t)
134184 , MonadSample t (PullM t)
135185 , MonadFix (PushM t)
136− , Functor (Event t)
137− , Functor (Behavior t)
138186 , Functor (Dynamic t)
139187 , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
140188 , Monad (Dynamic t)
189+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
190+ , t ~ SpiderTimeline Global
191+#endif
141192 ) => Reflex t where
142193 -- | A container for a value that can change over time. 'Behavior's can be
143194 -- sampled at will, but it is not possible to be notified when they change
@@ -155,7 +206,12 @@ class ( MonadHold t (PushM t)
155206 -- notifications on changes. Basically a combination of a 'Behavior' and an
156207 -- 'Event', with a rule that the 'Behavior' will change if and only if the
157208 -- 'Event' fires.
158− data Incremental t :: (* -> *) -> * -> *
209+ data Incremental t :: * -> *
210+ -- | A monad for doing complex push-based calculations efficiently
211+ type PushM t :: * -> *
212+ -- | A monad for doing complex pull-based calculations efficiently
213+ type PullM t :: * -> *
214+#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
159215 -- | An 'Event' with no occurrences
160216 never :: Event t a
161217 -- | Create a 'Behavior' that always has the given value
@@ -164,13 +220,9 @@ class ( MonadHold t (PushM t)
164220 -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
165221 -- (Just) or non-occurring (Nothing) result
166222 push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
167− -- | A monad for doing complex push-based calculations efficiently
168− type PushM t :: * -> *
169223 -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
170224 -- recomputed whenever any of the read 'Behavior's changes
171225 pull :: PullM t a -> Behavior t a
172− -- | A monad for doing complex pull-based calculations efficiently
173− type PullM t :: * -> *
174226 -- | Merge a collection of events; the resulting 'Event' will only occur if at
175227 -- least one input event is occuring, and will contain all of the input keys
176228 -- that are occurring simultaneously
@@ -195,52 +247,103 @@ class ( MonadHold t (PushM t)
195247 -- | Create a new 'Incremental'. The given 'PullM''s value must always change
196248 -- in the same way that the accumulated application of patches would change
197249 -- that value.
198− unsafeBuildIncremental :: Patch p => PullM t a -> Event t (p a) -> Incremental t p a
250+ unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
199251 -- | Create a merge whose parents can change over time
200− mergeIncremental :: GCompare k => Incremental t PatchDMap (DMap k (Event t)) -> Event t (DMap k Identity)
252+ mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
201253 -- | Extract the 'Behavior' component of an 'Incremental'
202− currentIncremental :: Patch p => Incremental t p a -> Behavior t a
254+ currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
203255 -- | Extract the 'Event' component of an 'Incremental'
204− updatedIncremental :: Patch p => Incremental t p a -> Event t (p a)
256+ updatedIncremental :: Patch p => Incremental t p -> Event t p
205257 -- | Convert an 'Incremental' to a 'Dynamic'
206− incrementalToDynamic :: Patch p => Incremental t p a -> Dynamic t a
207−
208−-- | A 'Patch' type represents a kind of change made to a datastructure.
209−class Patch p where
210− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
211− -- 'Nothing'.
212− apply :: p a -> a -> Maybe a
213−
214−instance Patch Identity where
215− apply (Identity a) _ = Just a
216−
217−-- | A set of changes to a 'DMap'. Only DMap types are allowed for @a@.
218−data PatchDMap a where
219− PatchDMap :: GCompare k => DMap k (Compose Maybe v) -> PatchDMap (DMap k v)
258+ incrementalToDynamic :: Patch p => Incremental t p -> Dynamic t (PatchTarget p)
259+ -- | Construct a 'Coercion' for a 'Behavior' given an 'Coercion' for its
260+ -- occurrence type
261+ behaviorCoercion :: Coercion a b -> Coercion (Behavior t a) (Behavior t b)
262+ -- | Construct a 'Coercion' for an 'Event' given an 'Coercion' for its
263+ -- occurrence type
264+ eventCoercion :: Coercion a b -> Coercion (Event t a) (Event t b)
265+ -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
266+ -- occurrence type
267+ dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
268+#endif
220269
221−instance GCompare k => Semigroup (PatchDMap (DMap k v)) where
222− PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
223− -- PatchDMap is idempotent, so stimes n is id for every n
224−#if MIN_VERSION_semigroups(0,17,0)
225− stimes = stimesIdempotentMonoid
226−#else
227− times1p n x = case compare n 0 of
228− LT -> error "stimesIdempotentMonoid: negative multiplier"
229− EQ -> mempty
230− GT -> x
270+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
271+instance t ~ SpiderTimeline Global => Reflex t where
272+ newtype Behavior t a = SpiderBehavior { unSpiderBehavior :: Reflex.Spider.Internal.Behavior Global a }
273+ newtype Event t a = SpiderEvent { unSpiderEvent :: Reflex.Spider.Internal.Event Global a }
274+ newtype Dynamic t a = SpiderDynamic { unSpiderDynamic :: Reflex.Spider.Internal.Dynamic Global (Identity a) } -- deriving (Functor, Applicative, Monad)
275+ newtype Incremental t p = SpiderIncremental { unSpiderIncremental :: Reflex.Spider.Internal.Dynamic Global p }
276+ type PullM t = SpiderPullM Global
277+ type PushM t = SpiderPushM Global
278+never :: Reflex t => Event t a
279+constant :: Reflex t => a -> Behavior t a
280+push :: Reflex t => (a -> PushM t (Maybe b)) -> Event t a -> Event t b
281+pull :: Reflex t => PullM t a -> Behavior t a
282+merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
283+fan :: (Reflex t, GCompare k) => Event t (DMap k Identity) -> EventSelector t k
284+switch :: Reflex t => Behavior t (Event t a) -> Event t a
285+coincidence :: Reflex t => Event t (Event t a) -> Event t a
286+current :: Reflex t => Dynamic t a -> Behavior t a
287+updated :: Reflex t => Dynamic t a -> Event t a
288+unsafeBuildDynamic :: Reflex t => PullM t a -> Event t a -> Dynamic t a
289+unsafeBuildIncremental :: (Reflex t, Patch p) => PullM t (PatchTarget p) -> Event t p -> Incremental t p
290+mergeIncremental :: (Reflex t, GCompare k) => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
291+currentIncremental :: (Reflex t, Patch p) => Incremental t p -> Behavior t (PatchTarget p)
292+updatedIncremental :: (Reflex t, Patch p) => Incremental t p -> Event t p
293+incrementalToDynamic :: (Reflex t, Patch p) => Incremental t p -> Dynamic t (PatchTarget p)
294+behaviorCoercion :: Reflex t => Coercion a b -> Coercion (Behavior t a) (Behavior t b)
295+eventCoercion :: Reflex t => Coercion a b -> Coercion (Event t a) (Event t b)
296+dynamicCoercion :: Reflex t => Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
297+{-# INLINE never #-}
298+never = SpiderEvent S.eventNever
299+{-# INLINE constant #-}
300+constant = SpiderBehavior . S.behaviorConst
301+{-# INLINE push #-}
302+push f = SpiderEvent . S.push (coerce f) . unSpiderEvent
303+{-# INLINE pull #-}
304+pull = SpiderBehavior . S.pull . coerce
305+{-# INLINE merge #-}
306+merge = SpiderEvent . S.merge . S.dynamicConst . (coerce :: DMap k (Event (SpiderTimeline x)) -> DMap k (S.Event x))
307+{-# INLINE fan #-}
308+fan e = EventSelector $ SpiderEvent . S.select (S.fan (unSpiderEvent e))
309+{-# INLINE switch #-}
310+switch = SpiderEvent . S.switch . (coerce :: S.Behavior x (Event (SpiderTimeline x) a) -> S.Behavior x (S.Event x a)) . unSpiderBehavior
311+{-# INLINE coincidence #-}
312+coincidence = SpiderEvent . S.coincidence . (coerce :: S.Event x (Event (SpiderTimeline x) a) -> S.Event x (S.Event x a)) . unSpiderEvent
313+{-# INLINE current #-}
314+current = SpiderBehavior . S.dynamicCurrent . unSpiderDynamic
315+{-# INLINE updated #-}
316+updated = SpiderEvent . fmap runIdentity . dynamicUpdated . unSpiderDynamic
317+{-# INLINE unsafeBuildDynamic #-}
318+unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
319+{-# INLINE unsafeBuildIncremental #-}
320+unsafeBuildIncremental readV0 dv = SpiderIncremental $ S.dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
321+{-# INLINE mergeIncremental #-}
322+mergeIncremental = SpiderEvent . S.merge . (unsafeCoerce :: S.Dynamic x (PatchDMap k (Event (SpiderTimeline x))) -> S.Dynamic x (PatchDMap k (S.Event x))) . unSpiderIncremental
323+{-# INLINE currentIncremental #-}
324+currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
325+{-# INLINE updatedIncremental #-}
326+updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
327+{-# INLINE incrementalToDynamic #-}
328+incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip S.push (dynamicUpdated i) $ \p -> do
329+ c <- readBehaviorUntracked $ dynamicCurrent i
330+ return $ Identity <$> apply p c
331+eventCoercion Coercion = Coercion
332+behaviorCoercion Coercion = Coercion
333+dynamicCoercion = unsafeCoerce
231334#endif
232335
233−instance GCompare k => Monoid (PatchDMap (DMap k v)) where
234− mempty = PatchDMap mempty
235− mappend = (<>)
336+-- | Coerce a 'Behavior' between representationally-equivalent value types
337+coerceBehavior :: (Reflex t, Coercible a b) => Behavior t a -> Behavior t b
338+coerceBehavior = coerceWith $ behaviorCoercion Coercion
339+
340+-- | Coerce an 'Event' between representationally-equivalent occurrence types
341+coerceEvent :: (Reflex t, Coercible a b) => Event t a -> Event t b
342+coerceEvent = coerceWith $ eventCoercion Coercion
236343
237−instance Patch PatchDMap where
238− 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?
239− where insertions = DMap.mapMaybeWithKey (const $ getCompose) diff
240− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getCompose) diff
241− nothingToJust = \case
242− Nothing -> Just $ Constant ()
243− Just _ -> Nothing
344+-- | Coerce a 'Dynamic' between representationally-equivalent value types
345+coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
346+coerceDynamic = coerceWith $ dynamicCoercion Coercion
244347
245348-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
246349-- _must_ change when and only when the 'Event' fires, such that the
@@ -279,7 +382,7 @@ class MonadSample t m => MonadHold t m where
279382 holdDyn :: a -> Event t a -> m (Dynamic t a)
280383 -- | Create an 'Incremental' value using the given initial value that changes
281384 -- every time the 'Event' occurs.
282− holdIncremental :: Patch p => a -> Event t (p a) -> m (Incremental t p a)
385+ holdIncremental :: Patch p => PatchTarget p -> Event t p -> m (Incremental t p)
283386
284387-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
285388-- key. This is much more efficient than filtering for each key with
@@ -412,28 +515,6 @@ instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
412515 times1p n = fmap $ times1p n
413516#endif
414517
415−--TODO: See if there's a better class in the standard libraries already
416−
417−-- | A class for values that combines filtering and mapping using 'Maybe'.
418−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
419−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
420−-- and e.g. 'Event' is instance of neither.
421−--
422−-- A definition of 'fmapMaybe' must satisfy the following laws:
423−--
424−-- [/identity/]
425−-- @'fmapMaybe' 'Just' ≡ 'id'@
426−--
427−-- [/composition/]
428−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
429−
430−class Functor f => FunctorMaybe f where
431− -- | Combined mapping and filtering function.
432− fmapMaybe :: (a -> Maybe b) -> f a -> f b
433−
434−instance FunctorMaybe [] where
435− fmapMaybe f = catMaybes . fmap f
436−
437518-- | Flipped version of 'fmapMaybe'.
438519fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
439520fforMaybe = flip fmapMaybe
@@ -458,9 +539,11 @@ instance Reflex t => Bind (Event t) where
458539 join = coincidence
459540
460541instance Reflex t => Functor (Event t) where
542+ {-# INLINE fmap #-}
461543 fmap f = fmapMaybe $ Just . f
462544
463545instance Reflex t => FunctorMaybe (Event t) where
546+ {-# INLINE fmapMaybe #-}
464547 fmapMaybe f = push $ return . f
465548
466549-- | Never: @'zero' = 'never'@.
@@ -494,7 +577,6 @@ headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
494577headE e = do
495578 rec be <- hold e $ fmap (const never) e'
496579 let e' = switch be
497− e' `seq` return ()
498580 return e'
499581
500582-- | Create a new 'Event' that occurs on all but the first occurence of the
@@ -532,54 +614,6 @@ traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
532614traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
533615traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
534616
535−-- | Tag type for 'Either' to use it as a 'DSum'.
536−data EitherTag l r a where
537− LeftTag :: EitherTag l r l
538− RightTag :: EitherTag l r r
539−
540−instance GEq (EitherTag l r) where
541− geq a b = case (a, b) of
542− (LeftTag, LeftTag) -> Just Refl
543− (RightTag, RightTag) -> Just Refl
544− _ -> Nothing
545−
546−instance GCompare (EitherTag l r) where
547− gcompare a b = case (a, b) of
548− (LeftTag, LeftTag) -> GEQ
549− (LeftTag, RightTag) -> GLT
550− (RightTag, LeftTag) -> GGT
551− (RightTag, RightTag) -> GEQ
552−
553−instance GShow (EitherTag l r) where
554− gshowsPrec _ a = case a of
555− LeftTag -> showString "LeftTag"
556− RightTag -> showString "RightTag"
557−
558−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
559− showTaggedPrec t n (Identity a) = case t of
560− LeftTag -> showsPrec n a
561− RightTag -> showsPrec n a
562−
563−-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
564−eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
565−eitherToDSum = \case
566− Left a -> (LeftTag :=> Identity a)
567− Right b -> (RightTag :=> Identity b)
568−
569−-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
570−dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
571−dsumToEither = \case
572− (LeftTag :=> Identity a) -> Left a
573− (RightTag :=> Identity b) -> Right b
574−
575−-- | Extract the values of a 'DMap' of 'EitherTag's.
576−dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
577−dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
578− (Nothing, Nothing) -> Nothing
579− (Just (Identity a), Nothing) -> Just $ This a
580− (Nothing, Just (Identity b)) -> Just $ That b
581− (Just (Identity a), Just (Identity b)) -> Just $ These a b
582−
583617instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
584618 (<>) = alignWith (mergeThese (<>))
585619 sconcat = fmap sconcat . mergeList . toList
@@ -597,6 +631,7 @@ instance (Semigroup a, Reflex t) => Monoid (Event t a) where
597631-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
598632-- list occurs. If multiple occur at the same time they are folded from the left
599633-- with the given function.
634+{-# INLINE mergeWith #-}
600635mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
601636mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
602637 . merge
@@ -607,6 +642,7 @@ mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v)
607642-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
608643-- list occurs. If multiple occur at the same time the value is the value of the
609644-- leftmost event.
645+{-# INLINE leftmost #-}
610646leftmost :: Reflex t => [Event t a] -> Event t a
611647leftmost = mergeWith const
612648
@@ -762,21 +798,21 @@ class Reflex t => Accumulator t f | f -> t where
762798
763799-- | Accumulate occurrences of an 'Event', producing an output occurrence each
764800-- time. Discard the underlying 'Accumulator'.
765−mapAccum_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Event t c)
801+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)
766802mapAccum_ f z e = do
767803 (_ :: Behavior t a, result) <- mapAccum f z e
768804 return result
769805
770806-- | Accumulate occurrences of an 'Event', possibly producing an output
771807-- occurrence each time. Discard the underlying 'Accumulator'.
772−mapAccumMaybe_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
808+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)
773809mapAccumMaybe_ f z e = do
774810 (_ :: Behavior t a, result) <- mapAccumMaybe f z e
775811 return result
776812
777813-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
778814-- an output occurrence each time. Discard the underlying 'Accumulator'.
779−mapAccumM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Event t c)
815+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)
780816mapAccumM_ f z e = do
781817 (_ :: Behavior t a, result) <- mapAccumM f z e
782818 return result
@@ -784,7 +820,7 @@ mapAccumM_ f z e = do
784820-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
785821-- producing an output occurrence each time. Discard the underlying
786822-- 'Accumulator'.
787−mapAccumMaybeM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
823+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)
788824mapAccumMaybeM_ f z e = do
789825 (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
790826 return result
@@ -824,6 +860,21 @@ zipListWithEvent f l e = do
824860 _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
825861 mapAccumMaybe_ f' l e
826862
863+class (Reflex t, Monad m) => MonadAdjust t m | m -> t where
864+ runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
865+ sequenceDMapWithAdjust :: GCompare k => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
866+
867+instance (Reflex t, MonadAdjust t m) => MonadAdjust t (ReaderT r m) where
868+ runWithReplace a0 a' = do
869+ r <- ask
870+ lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
871+ sequenceDMapWithAdjust dm0 dm' = do
872+ r <- ask
873+ let loweredDm0 = DMap.map (`runReaderT` r) dm0
874+ loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
875+ DMap.map (\(ComposeMaybe mv) -> ComposeMaybe $ fmap (`runReaderT` r) mv) p
876+ lift $ sequenceDMapWithAdjust loweredDm0 loweredDm'
877+
827878--------------------------------------------------------------------------------
828879-- Deprecated functions
829880--------------------------------------------------------------------------------
src/Reflex/Deletable/Class.hsdeleted−17
@@ -1,17 +0,0 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE FunctionalDependencies #-}
3−{-# LANGUAGE MultiParamTypeClasses #-}
4−{-# LANGUAGE UndecidableInstances #-}
5−module Reflex.Deletable.Class where
6−
7−import Reflex.Class
8−
9−import Control.Monad.Reader
10−import Control.Monad.Trans.Control
11−
12−class (Reflex t, Monad m) => Deletable t m | m -> t where
13− deletable :: Event t () -> m a -> m a
14−
15−instance Deletable t m => Deletable t (ReaderT r m) where
16− {-# INLINABLE deletable #-}
17− deletable = liftThrough . deletable
src/Reflex/Dynamic.hs+2−1
@@ -12,6 +12,7 @@
1212{-# LANGUAGE TypeFamilies #-}
1313{-# LANGUAGE TypeOperators #-}
1414{-# LANGUAGE UndecidableInstances #-}
15+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1516-- | This module contains various functions for working with 'Dynamic' values.
1617-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
1718module Reflex.Dynamic
@@ -38,8 +39,8 @@ module Reflex.Dynamic
3839 , traceDyn
3940 , traceDynWith
4041 , splitDynPure
41− , distributeDMapOverDynPure
4242 , distributeMapOverDynPure
43+ , distributeDMapOverDynPure
4344 , distributeListOverDynPure
4445 , Demux
4546 , demux
src/Reflex/Dynamic/TH.hs+1
@@ -4,6 +4,7 @@
44{-# LANGUAGE ScopedTypeVariables #-}
55{-# LANGUAGE TemplateHaskell #-}
66{-# LANGUAGE TypeOperators #-}
7+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
78-- | Template Haskell helper functions for building complex 'Dynamic' values.
89module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
910
src/Reflex/Dynamic/Uniq.hs+1
@@ -1,6 +1,7 @@
11{-# LANGUAGE FlexibleInstances #-}
22{-# LANGUAGE MagicHash #-}
33{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
45-- | This module provides a variation of 'Dynamic' values that uses cheap
56-- pointer equality checks to reduce the amount of signal propagation needed.
67module Reflex.Dynamic.Uniq
src/Reflex/DynamicWriter.hs+69−52
@@ -9,83 +9,59 @@
99{-# LANGUAGE ScopedTypeVariables #-}
1010{-# LANGUAGE TypeFamilies #-}
1111{-# LANGUAGE UndecidableInstances #-}
12+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1213module Reflex.DynamicWriter where
1314
1415import Control.Monad.Exception
16+import Control.Monad.Identity
1517import Control.Monad.IO.Class
1618import Control.Monad.Reader
1719import Control.Monad.Ref
1820import Control.Monad.State.Strict
19−import Data.Dependent.Map (DMap)
20−import Data.Functor.Compose
21+import Data.Dependent.Map (DMap, DSum (..))
22+import qualified Data.Dependent.Map as DMap
2123import Data.Functor.Misc
2224import Data.Map (Map)
2325import qualified Data.Map as Map
2426import Data.Semigroup
27+import Data.Some (Some)
28+import qualified Data.Some as Some
2529import Reflex.Class
26−import Reflex.Deletable.Class
2730import Reflex.Dynamic
31+import Reflex.Host.Class
2832import Reflex.PerformEvent.Class
2933import Reflex.PostBuild.Class
30−import Reflex.Host.Class
3134
3235instance MonadTrans (DynamicWriterT t w) where
3336 lift = DynamicWriterT . lift
3437
35−data PatchMap a where
36− PatchMap :: Ord k => Map k (Maybe v) -> PatchMap (Map k v)
37−
38−instance Patch PatchMap where
39− 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?
40− where insertions = Map.mapMaybeWithKey (const id) p
41− deletions = Map.mapMaybeWithKey (const nothingToJust) p
42− nothingToJust = \case
43− Nothing -> Just ()
44− Just _ -> Nothing
45−
46−instance Ord k => Semigroup (PatchMap (Map k v)) where
47− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
48− -- PatchMap is idempotent, so stimes n is id for every n
49−#if MIN_VERSION_semigroups(0,17,0)
50− stimes = stimesIdempotentMonoid
51−#else
52− times1p n x = case compare n 0 of
53− LT -> error "stimesIdempotentMonoid: negative multiplier"
54− EQ -> mempty
55− GT -> x
56−#endif
57−
58−instance Ord k => Monoid (PatchMap (Map k v)) where
59− mempty = PatchMap mempty
60− mappend = (<>)
61−
62−mconcatIncremental :: (Reflex t, MonadHold t m, MonadFix m, Monoid v) => Map k v -> Event t (PatchMap (Map k v)) -> m (Dynamic t v)
38+mconcatIncremental :: (Reflex t, MonadHold t m, MonadFix m, Monoid v, Ord k) => Map k v -> Event t (PatchMap k v) -> m (Dynamic t v)
6339mconcatIncremental m0 e = do
6440 d <- foldDynMaybe apply m0 e
6541 return $ fmap (mconcat . Map.elems) d
6642
67−mapIncrementalMapValuesWithKey :: Reflex t => (k -> v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
43+mapIncrementalMapValuesWithKey :: (Reflex t, Ord k) => (k -> v -> v') -> Incremental t (PatchMap k v) -> Incremental t (PatchMap k v')
6844mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
6945
70−mapIncrementalMapValues :: Reflex t => (v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
46+mapIncrementalMapValues :: (Reflex t, Ord k) => (v -> v') -> Incremental t (PatchMap k v) -> Incremental t (PatchMap k v')
7147mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
7248
73−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (a -> a') -> (p a -> p' a') -> Incremental t p a -> Incremental t p' a'
49+unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
7450unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
7551
76−incrementalExtractFunctorDMap :: Reflex t => Incremental t PatchMap (Map k (f v)) -> Incremental t PatchDMap (DMap (Const2 k v) f)
77−incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap Compose m
52+incrementalExtractFunctorDMap :: (Reflex t, Ord k) => Incremental t (PatchMap k (f v)) -> Incremental t (PatchDMap (Const2 k v) f)
53+incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap ComposeMaybe m
7854
79−mergeIncrementalMap :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Event t v)) -> Event t (Map k v)
55+mergeIncrementalMap :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t v)) -> Event t (Map k v)
8056mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
8157
82−holdIncrementalReplaceableMap :: forall t k v m. (MonadFix m, MonadHold t m, Reflex t, Ord k) => Map k (Replaceable t v) -> Event t (PatchMap (Map k (Replaceable t v))) -> m (Incremental t PatchMap (Map k v))
58+holdIncrementalReplaceableMap :: forall t k v m. (MonadFix m, MonadHold t m, Reflex t, Ord k) => Map k (Replaceable t v) -> Event t (PatchMap k (Replaceable t v)) -> m (Incremental t (PatchMap k v))
8359holdIncrementalReplaceableMap m0 m' = do
8460 rec vals <- holdIncremental m0 $ m' <> valChanges
8561 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
8662 return $ mapIncrementalMapValues _replaceable_value vals
8763
88−mergeDynIncremental :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Dynamic t v)) -> Incremental t PatchMap (Map k v)
64+mergeDynIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Dynamic t v)) -> Incremental t (PatchMap k v)
8965mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
9066 where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
9167 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
@@ -95,7 +71,7 @@ data Replaceable t w = Replaceable
9571 , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
9672 }
9773
98−type DynamicWriterAccumulator t w = [Replaceable t (Dynamic t w)]
74+type DynamicWriterAccumulator t w = [Dynamic t w]
9975
10076newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT (DynamicWriterAccumulator t w) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
10177
@@ -115,7 +91,12 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWrit
11591runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
11692runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
11793
118−mconcatIncrementalReplaceableDynMap :: forall m t k v. (MonadFix m, MonadHold t m, Reflex t, Monoid v, Ord k) => Map k (Replaceable t (Dynamic t v)) -> Event t (PatchMap (Map k (Replaceable t (Dynamic t v)))) -> Event t () -> m (Replaceable t (Dynamic t v))
94+mconcatIncrementalReplaceableDynMap :: forall m t k v.
95+ (MonadFix m, MonadHold t m, Reflex t, Monoid v, Ord k)
96+ => Map k (Replaceable t (Dynamic t v))
97+ -> Event t (PatchMap k (Replaceable t (Dynamic t v)))
98+ -> Event t ()
99+ -> m (Replaceable t (Dynamic t v))
119100mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
120101 rec vals <- holdIncremental m0 $ m' <> valChanges
121102 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
@@ -130,17 +111,16 @@ mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
130111 result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
131112 return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
132113
133−runDynamicWriterT :: (MonadFix m, Reflex t, MonadHold t m, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
114+runDynamicWriterT :: (MonadFix m, Reflex t, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
134115runDynamicWriterT (DynamicWriterT a) = do
135116 (result, ws) <- runStateT a []
136− Replaceable w _ <- mconcatIncrementalReplaceableDynMap (Map.fromList $ zip [1 :: Int ..] $ reverse ws) never never
137− return (result, w)
117+ return (result, mconcat $ reverse ws)
138118
139119class Monad m => MonadDynamicWriter t w m | m -> t w where
140120 tellDyn :: Dynamic t w -> m ()
141121
142122instance (Monad m, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
143− tellDyn w = DynamicWriterT $ modify (Replaceable w never :)
123+ tellDyn w = DynamicWriterT $ modify (w :)
144124
145125instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
146126 ask = lift ask
@@ -154,12 +134,6 @@ liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
154134 put newS
155135 return (b, a)
156136
157−instance (Deletable t m, Reflex t, Monoid w, MonadHold t m, MonadFix m) => Deletable t (DynamicWriterT t w m) where
158− deletable delete child = do
159− (result, output) <- lift $ deletable delete $ runDynamicWriterT child
160− DynamicWriterT $ modify (Replaceable output (Nothing <$ delete) :)
161− return result
162−
163137instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
164138 type Performable (DynamicWriterT t w m) = Performable m
165139 performEvent_ = lift . performEvent_
@@ -179,3 +153,46 @@ instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
179153instance MonadState s m => MonadState s (DynamicWriterT t w m) where
180154 get = lift get
181155 put = lift . put
156+
157+newtype DynamicWriterTLoweredResult t w v = DynamicWriterTLoweredResult { unDynamicWriterTLoweredResult :: (v, Dynamic t w) }
158+
159+instance (MonadAdjust t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => MonadAdjust t (DynamicWriterT t w m) where
160+ runWithReplace a0 a' = do
161+ (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
162+ tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
163+ return (fst result0, fst <$> result')
164+ sequenceDMapWithAdjust (dm0 :: DMap k (DynamicWriterT t w m)) dm' = do
165+ let loweredDm0 = mapKeyValuePairsMonotonic (\(k :=> v) -> WrapArg k :=> fmap DynamicWriterTLoweredResult (runDynamicWriterT v)) dm0
166+ let loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
167+ mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mv) -> WrapArg k :=> ComposeMaybe (fmap (fmap DynamicWriterTLoweredResult . runDynamicWriterT) mv)) p
168+ (result0, result') <- lift $ sequenceDMapWithAdjust loweredDm0 loweredDm'
169+ let getValue (DynamicWriterTLoweredResult (v, _)) = v
170+ getWritten (DynamicWriterTLoweredResult (_, w)) = w
171+ liftedResult0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity r) -> k :=> Identity (getValue r)) result0
172+ liftedResult' = ffor result' $ \(PatchDMap p) -> PatchDMap $
173+ mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (Identity . getValue . runIdentity) mr)) p
174+ liftedWritten0 :: Map (Some k) (Dynamic t w)
175+ liftedWritten0 = Map.fromDistinctAscList $ fmap (\(WrapArg k :=> Identity r) -> (Some.This k, getWritten r)) $ DMap.toList result0
176+ liftedWritten' = ffor result' $ \(PatchDMap p) -> PatchMap $
177+ Map.fromDistinctAscList $ fmap (\(WrapArg k :=> ComposeMaybe mr) -> (Some.This k, fmap (getWritten . runIdentity) mr)) $ DMap.toList p
178+ --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
179+ i <- holdIncremental liftedWritten0 liftedWritten'
180+ tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
181+ return (liftedResult0, liftedResult')
182+
183+withDynamicWriterT :: (Monoid w, Reflex t, MonadHold t m, MonadFix m)
184+ => (w -> w')
185+ -> DynamicWriterT t w m a
186+ -> DynamicWriterT t w' m a
187+withDynamicWriterT f dw = do
188+ (r, d) <- lift $ do
189+ (r, d) <- runDynamicWriterT dw
190+ let d' = fmap f d
191+ return (r, d')
192+ tellDyn d
193+ return r
194+
195+instance MonadRequest t m => MonadRequest t (DynamicWriterT t w m) where
196+ type Request (DynamicWriterT t w m) = Request m
197+ type Response (DynamicWriterT t w m) = Response m
198+ withRequesting f = DynamicWriterT $ withRequesting $ unDynamicWriterT . f
src/Reflex/FunctorMaybe.hsnew file+25
@@ -0,0 +1,25 @@
1+module Reflex.FunctorMaybe where
2+
3+import Data.Maybe
4+
5+--TODO: See if there's a better class in the standard libraries already
6+
7+-- | A class for values that combines filtering and mapping using 'Maybe'.
8+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
9+-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
10+-- and e.g. 'Event' is instance of neither.
11+--
12+-- A definition of 'fmapMaybe' must satisfy the following laws:
13+--
14+-- [/identity/]
15+-- @'fmapMaybe' 'Just' ≡ 'id'@
16+--
17+-- [/composition/]
18+-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
19+class FunctorMaybe f where
20+ -- | Combined mapping and filtering function.
21+ fmapMaybe :: (a -> Maybe b) -> f a -> f b
22+
23+instance FunctorMaybe [] where
24+ fmapMaybe f = catMaybes . fmap f
25+
src/Reflex/Host/Class.hs+13
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE ExistentialQuantification #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE FlexibleInstances #-}
@@ -9,6 +10,7 @@
910{-# LANGUAGE ScopedTypeVariables #-}
1011{-# LANGUAGE TypeFamilies #-}
1112{-# LANGUAGE UndecidableInstances #-}
13+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1214-- | This module provides the interface for hosting 'Reflex' engines. This
1315-- should only be necessary if you're writing a binding or some other library
1416-- that provides a core event loop.
@@ -43,9 +45,20 @@ import Data.Dependent.Sum (DSum (..))
4345import Data.GADT.Compare
4446import Data.Monoid
4547
48+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
49+import Reflex.Spider.Internal (HasSpiderTimeline, SpiderHost (..), SpiderHostFrame (..), Subscriber (..), SpiderEventHandle (..), RootTrigger, runFrame, subscribe, scheduleClear, newEventWithTriggerIO, newFanEventWithTriggerIO, EventM (..), run)
50+import qualified Reflex.Spider.Internal
51+import Data.IORef
52+import Control.Monad.Primitive (touch)
53+#endif
54+
4655-- Note: this import must come last to silence warnings from AMP
4756import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
4857
58+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
59+#include "../Spider/SpiderTimelineHost.include.hs"
60+#endif
61+
4962-- | Framework implementation support class for the reflex implementation
5063-- represented by @t@.
5164class ( Reflex t
src/Reflex/Optimizer.hsnew file+40
@@ -0,0 +1,40 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+module Reflex.Optimizer (plugin) where
5+#ifdef ghcjs_HOST_OS
6+
7+import Plugins
8+
9+plugin :: Plugin
10+plugin = undefined
11+
12+#else
13+
14+import Control.Arrow
15+import CoreMonad
16+import Data.String
17+import GhcPlugins
18+
19+plugin :: Plugin
20+plugin = defaultPlugin { installCoreToDos = install }
21+
22+install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
23+install [] p = return $ makeInlinable : p
24+install options@(_:_) p = do
25+ warnMsg $ "Reflex.Optimizer: ignoring " <> fromString (show $ length options) <> " command-line options"
26+ install [] p
27+
28+makeInlinable :: CoreToDo
29+makeInlinable = CoreDoPluginPass "MakeInlinable" $ \modGuts -> do
30+ let f v = setIdInfo v $ let i = idInfo v in
31+ setInlinePragInfo i $ let p = inlinePragInfo i in
32+ if isDefaultInlinePragma p
33+ then defaultInlinePragma { inl_inline = Inlinable }
34+ else p
35+ newBinds = flip map (mg_binds modGuts) $ \case
36+ NonRec b e -> NonRec (f b) e
37+ Rec bes -> Rec $ map (first f) bes
38+ return $ modGuts { mg_binds = newBinds }
39+
40+#endif
src/Reflex/Patch.hsnew file+92
@@ -0,0 +1,92 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE StandaloneDeriving #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE DeriveFunctor #-}
6+module Reflex.Patch
7+ ( Patch (..)
8+ , PatchDMap (..)
9+ , ComposeMaybe (..)
10+ , PatchMap (..)
11+ ) where
12+
13+import Control.Monad.Identity
14+import Data.Dependent.Map (DMap, GCompare (..))
15+import qualified Data.Dependent.Map as DMap
16+import Data.Functor.Constant
17+import Data.Map (Map)
18+import qualified Data.Map as Map
19+import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid, (<>))
20+
21+-- | A 'Patch' type represents a kind of change made to a datastructure.
22+class Patch p where
23+ type PatchTarget p :: *
24+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
25+ -- 'Nothing'.
26+ apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
27+
28+-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
29+-- make the 'f' parameter have a nominal type role. We need f to be
30+-- representational so that we can use safe 'coerce'.
31+newtype ComposeMaybe f a = ComposeMaybe { getComposeMaybe :: Maybe (f a) }
32+
33+deriving instance Functor f => Functor (ComposeMaybe f)
34+
35+instance Patch (Identity a) where
36+ type PatchTarget (Identity a) = a
37+ apply (Identity a) _ = Just a
38+
39+-- | A set of changes to a 'DMap'. Only DMap types are allowed for @a@.
40+newtype PatchDMap k v = PatchDMap (DMap k (ComposeMaybe v))
41+
42+instance GCompare k => Semigroup (PatchDMap k v) where
43+ PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
44+ -- PatchDMap is idempotent, so stimes n is id for every n
45+#if MIN_VERSION_semigroups(0,17,0)
46+ stimes = stimesIdempotentMonoid
47+#else
48+ times1p n x = case compare n 0 of
49+ LT -> error "stimesIdempotentMonoid: negative multiplier"
50+ EQ -> mempty
51+ GT -> x
52+#endif
53+
54+instance GCompare k => Monoid (PatchDMap k v) where
55+ mempty = PatchDMap mempty
56+ mappend = (<>)
57+
58+instance GCompare k => Patch (PatchDMap k v) where
59+ type PatchTarget (PatchDMap k v) = DMap k v
60+ apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
61+ where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
62+ deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
63+ nothingToJust = \case
64+ Nothing -> Just $ Constant ()
65+ Just _ -> Nothing
66+
67+newtype PatchMap k v = PatchMap (Map k (Maybe v))
68+
69+instance Ord k => Patch (PatchMap k v) where
70+ type PatchTarget (PatchMap k v) = Map k v
71+ apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
72+ where insertions = Map.mapMaybeWithKey (const id) p
73+ deletions = Map.mapMaybeWithKey (const nothingToJust) p
74+ nothingToJust = \case
75+ Nothing -> Just ()
76+ Just _ -> Nothing
77+
78+instance Ord k => Semigroup (PatchMap k v) where
79+ PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
80+ -- PatchMap is idempotent, so stimes n is id for every n
81+#if MIN_VERSION_semigroups(0,17,0)
82+ stimes = stimesIdempotentMonoid
83+#else
84+ times1p n x = case compare n 0 of
85+ LT -> error "stimesIdempotentMonoid: negative multiplier"
86+ EQ -> mempty
87+ GT -> x
88+#endif
89+
90+instance Ord k => Monoid (PatchMap k v) where
91+ mempty = PatchMap mempty
92+ mappend = (<>)
src/Reflex/PerformEvent/Base.hs+227−78
@@ -1,7 +1,10 @@
11{-# LANGUAGE FlexibleContexts #-}
22{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE GADTs #-}
34{-# LANGUAGE GeneralizedNewtypeDeriving #-}
45{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE PartialTypeSignatures #-}
7+{-# LANGUAGE PolyKinds #-}
58{-# LANGUAGE RankNTypes #-}
69{-# LANGUAGE RecursiveDo #-}
710{-# LANGUAGE ScopedTypeVariables #-}
@@ -9,34 +12,39 @@
912{-# LANGUAGE TypeFamilies #-}
1013{-# LANGUAGE TypeOperators #-}
1114{-# LANGUAGE UndecidableInstances #-}
15+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1216module Reflex.PerformEvent.Base where
1317
1418import Reflex.Class
15−import Reflex.Deletable.Class
1619import Reflex.Host.Class
1720import Reflex.PerformEvent.Class
21+import Reflex.PostBuild.Class
1822
1923import Control.Lens
2024import Control.Monad.Exception
2125import Control.Monad.Identity
26+import Control.Monad.Primitive
2227import Control.Monad.Ref
2328import Control.Monad.State.Strict
24−import Data.Align
25−import Data.Dependent.Map (DMap)
29+import Control.Monad.Reader
30+import Data.Coerce
31+import Data.Dependent.Map (DMap, GCompare (..), Some)
2632import qualified Data.Dependent.Map as DMap
2733import Data.Dependent.Sum
28−import Data.Functor.Compose
2934import Data.Functor.Misc
35+import qualified Data.List.NonEmpty as NonEmpty
3036import Data.Maybe
3137import Data.Semigroup
38+import qualified Data.Some as Some
3239import Data.These
33−import Data.Word
40+import Data.Tuple
41+import Data.Unique.Tag
3442
3543newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
3644
3745newtype 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?
3846
39−newtype PerformEventT t m a = PerformEventT { unPerformEventT :: StateT [Event t (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())] (HostFrame t) a }
47+newtype PerformEventT t m a = PerformEventT { unPerformEventT :: RequestT t (HostFrame t) Identity (HostFrame t) a }
4048
4149deriving instance ReflexHost t => Functor (PerformEventT t m)
4250deriving instance ReflexHost t => Applicative (PerformEventT t m)
@@ -45,29 +53,31 @@ deriving instance ReflexHost t => MonadFix (PerformEventT t m)
4553deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
4654deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
4755
48−instance (ReflexHost t, MonadAsyncException (HostFrame t)) => MonadAsyncException (PerformEventT t m) where
49− mask f = PerformEventT $ mask $ \unmask -> unPerformEventT $ f $ PerformEventT . unmask . unPerformEventT
56+instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m) where
57+ type PrimState (PerformEventT t m) = PrimState (HostFrame t)
58+ primitive = PerformEventT . lift . primitive
5059
51−instance (ReflexHost t, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEvent t (PerformEventT t m) where
52− type Performable (PerformEventT t m) = PerformEventT t m
60+instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent t (PerformEventT t m) where
61+ type Performable (PerformEventT t m) = HostFrame t
5362 {-# INLINABLE performEvent_ #-}
54− performEvent_ = PerformEventT . modify . (:) . fmap (This . (>> return []))
63+ performEvent_ = PerformEventT . requesting_
5564 {-# INLINABLE performEvent #-}
56− performEvent e = PerformEventT $ do
57− (eResult, reResultTrigger) <- newEventWithTriggerRef
58− modify $ (:) $ ffor e $ \o -> This $ do
59− result <- o
60− return [EventTriggerRef reResultTrigger :=> Identity result]
61− return eResult
62−
63−instance (ReflexHost t, Monad (HostFrame t), Ref (HostFrame t) ~ Ref IO, Ref m ~ Ref IO) => Deletable t (PerformEventT t m) where
64− {-# INLINABLE deletable #-}
65− deletable d a = PerformEventT $ do
66− (result, reverseEventsToPerform) <- lift $ runStateT (unPerformEventT a) []
67− let (numberedEvents, _) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
68− eventToPerform <- lift $ mergePerformEventsAndAdd (DMap.fromList numberedEvents) never
69− modify (align eventToPerform d :)
70− return result
65+ performEvent = PerformEventT . fmap (fmap runIdentity) . requesting
66+
67+instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
68+ runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequestTWith f (coerce outerA0) (coerceEvent outerA')
69+ where f :: HostFrame t a -> Event t (HostFrame t b) -> RequestT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
70+ f a0 a' = do
71+ result0 <- lift a0
72+ result' <- requesting a'
73+ return (result0, fmap runIdentity result')
74+ sequenceDMapWithAdjust (outerDm0 :: DMap k (PerformEventT t m)) outerDm' = PerformEventT $ sequenceDMapWithAdjustRequestTWith f (coerce outerDm0) (coerceEvent outerDm')
75+ where f :: DMap k' (HostFrame t) -> Event t (PatchDMap k' (HostFrame t)) -> RequestT t (HostFrame t) Identity (HostFrame t) (DMap k' Identity, Event t (PatchDMap k' Identity))
76+ f dm0 dm' = do
77+ result0 <- lift $ DMap.traverseWithKey (\_ v -> Identity <$> v) dm0
78+ result' <- requesting $ ffor dm' $ \(PatchDMap p) -> do
79+ PatchDMap <$> DMap.traverseWithKey (\_ (ComposeMaybe mv) -> ComposeMaybe <$> mapM (fmap Identity) mv) p
80+ return (result0, fmap runIdentity result')
7181
7282instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
7383 {-# INLINABLE newEventWithTrigger #-}
@@ -75,55 +85,19 @@ instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
7585 {-# INLINABLE newFanEventWithTrigger #-}
7686 newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
7787
78−{-# INLINABLE numberWith #-}
79−numberWith :: (Enum n, Traversable t) => (n -> a -> b) -> t a -> n -> (t b, n)
80−numberWith f t n0 = flip runState n0 $ forM t $ \x -> state $ \n -> (f n x, succ n)
81−
82−{-# INLINABLE mergePerformEventsAndAdd #-}
83−mergePerformEventsAndAdd :: forall t m. ReflexHost t
84− => DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)
85− -> Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
86− -> HostFrame t (Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity]))
87−mergePerformEventsAndAdd initial eAddNew = do
88− rec events :: Incremental t PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t))
89− <- holdIncremental initial $ eAddNew <> eDelete --TODO: Eliminate empty firings
90− let event = mergeWith DMap.union [mergeIncremental events, coincidence (fmap (\(PatchDMap m) -> merge $ DMap.mapMaybeWithKey (const getCompose) m) eAddNew)]
91− eventToPerform :: Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity])
92− eventToPerform = ffor event $
93− fmap concat .
94− sequence .
95− fmapMaybe (\(Const2 _ :=> Identity x) -> x ^? here) .
96− DMap.toList
97− eDelete :: Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
98− eDelete = fforMaybe event $
99− fmap (PatchDMap . DMap.fromList) .
100− (\l -> if null l then Nothing else Just l) .
101− fmapMaybe (\(Const2 k :=> Identity x) -> (Const2 k :=> Compose Nothing) <$ x ^? there) .
102− DMap.toList
103− return eventToPerform
104−
105−{-# INLINABLE runPerformEventT #-}
106−runPerformEventT :: forall t m a. (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO, Ref m ~ Ref IO) => PerformEventT t m a -> HostFrame t (a, Event t (HostFrame t [DSum (EventTriggerRef t m) Identity]))
107−runPerformEventT (PerformEventT a) = do
108− (result, reverseEventsToPerform) <- runStateT a []
109− (eAddNew, reAddNewTrigger) <- newEventWithTriggerRef
110− let (numberedEvents, nextNumber) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
111− nextNumberRef <- newRef nextNumber
112− let runResult :: PerformEventT t m [DSum (EventTriggerRef t m) Identity] -> HostFrame t [DSum (EventTriggerRef t m) Identity]
113− runResult (PerformEventT r) = do
114− (followups, reverseNewEventsToPerform) <- runStateT r []
115− oldN <- readRef nextNumberRef
116− let (numberedNewEvents, newN) = numberWith (\n e -> Const2 n :=> Compose (Just e)) (reverse reverseNewEventsToPerform) oldN
117− if newN == oldN then return followups else do
118− writeRef nextNumberRef newN
119− return $ (EventTriggerRef reAddNewTrigger :=> Identity (PatchDMap (DMap.fromList numberedNewEvents))) : followups
120− eventToPerform <- mergePerformEventsAndAdd (DMap.fromList numberedEvents) eAddNew
121− return (result, runResult <$> eventToPerform)
122−
12388{-# INLINABLE hostPerformEventT #-}
124−hostPerformEventT :: forall t m a. (Monad m, MonadSubscribeEvent t m, MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEventT t m a -> m (a, FireCommand t m)
89+hostPerformEventT :: forall t m a.
90+ ( Monad m
91+ , MonadSubscribeEvent t m
92+ , MonadReflexHost t m
93+ , MonadRef m
94+ , Ref m ~ Ref IO
95+ )
96+ => PerformEventT t m a
97+ -> m (a, FireCommand t m)
12598hostPerformEventT a = do
126− (result, eventToPerform) <- runHostFrame $ runPerformEventT a
99+ (response, responseTrigger) <- newEventWithTriggerRef
100+ (result, eventToPerform) <- runHostFrame $ runRequestT (unPerformEventT a) response
127101 eventToPerformHandle <- subscribeEvent eventToPerform
128102 return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
129103 let go :: [DSum (EventTrigger t) Identity] -> m [a']
@@ -135,11 +109,12 @@ hostPerformEventT a = do
135109 case mToPerform of
136110 Nothing -> return [result']
137111 Just toPerform -> do
138− followupEventTriggerRefs <- runHostFrame toPerform
139− followupEventTriggers <- forM followupEventTriggerRefs $ \(EventTriggerRef rt :=> x) -> do
140− mt <- readRef rt
141− return $ fmap (:=> x) mt
142− fmap (result':) $ go $ catMaybes followupEventTriggers
112+ responses <- runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
113+ mrt <- readRef responseTrigger
114+ let followupEventTriggers = case mrt of
115+ Just rt -> [rt :=> Identity responses]
116+ Nothing -> []
117+ (result':) <$> go followupEventTriggers
143118 go triggers
144119
145120instance ReflexHost t => MonadSample t (PerformEventT t m) where
@@ -166,3 +141,177 @@ instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m)
166141instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
167142 {-# INLINABLE atomicModifyRef #-}
168143 atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
144+
145+--------------------------------------------------------------------------------
146+-- RequestT
147+--------------------------------------------------------------------------------
148+
149+newtype RequestT t request response m a
150+ = RequestT { unRequestT :: StateT [Event t (DMap (Tag (PrimState m)) request)] (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) a }
151+ deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException)
152+
153+instance MonadTrans (RequestT t request response) where
154+ lift = RequestT . lift . lift
155+
156+runRequestT :: forall t m a request response.
157+ ( Reflex t
158+ , Monad m
159+ )
160+ => RequestT t request response m a
161+ -> Event t (DMap (Tag (PrimState m)) response)
162+ -> m (a, Event t (DMap (Tag (PrimState m)) request))
163+runRequestT (RequestT a) responses = do
164+ (result, requests) <- runReaderT (runStateT a mempty) $ fan $ mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e) <$> responses
165+ return (result, mergeWith DMap.union requests)
166+
167+instance (Reflex t, PrimMonad m) => MonadRequest t (RequestT t request response m) where
168+ type Request (RequestT t request response m) = request
169+ type Response (RequestT t request response m) = response
170+ withRequesting a = do
171+ t <- lift newTag
172+ s <- RequestT ask
173+ (req, result) <- a $ select s $ WrapArg t
174+ RequestT $ modify $ (:) $ DMap.singleton t <$> req
175+ return result
176+
177+data DMapTransform (a :: *) k k' v v' = forall (b :: *). DMapTransform !(k a -> k' b) !(v a -> v' b)
178+
179+mapPatchKeysAndValuesMonotonic :: (forall a. DMapTransform a k k' v v') -> PatchDMap k v -> PatchDMap k' v'
180+mapPatchKeysAndValuesMonotonic x (PatchDMap p) = PatchDMap $ mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mv) -> case x of DMapTransform f g -> f k :=> ComposeMaybe (fmap g mv)) p
181+
182+mapKeysAndValuesMonotonic :: (forall a. DMapTransform a k k' v v') -> DMap k v -> DMap k' v'
183+mapKeysAndValuesMonotonic x = mapKeyValuePairsMonotonic $ \(k :=> v) -> case x of
184+ DMapTransform f g -> f k :=> g v
185+
186+instance MonadSample t m => MonadSample t (RequestT t request response m) where
187+ sample = lift . sample
188+
189+instance MonadHold t m => MonadHold t (RequestT t request response m) where
190+ hold v0 = lift . hold v0
191+ holdDyn v0 = lift . holdDyn v0
192+ holdIncremental v0 = lift . holdIncremental v0
193+
194+instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (RequestT t request response m) where
195+ runWithReplace = runWithReplaceRequestTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
196+ sequenceDMapWithAdjust = sequenceDMapWithAdjustRequestTWith $ \dm0 dm' -> lift $ sequenceDMapWithAdjust dm0 dm'
197+
198+runWithReplaceRequestTWith :: forall m t request response a b. (Reflex t, MonadHold t m)
199+ => (forall a' b'.
200+ m a'
201+ -> Event t (m b')
202+ -> RequestT t request response m (a', Event t b')
203+ )
204+ -> RequestT t request response m a
205+ -> Event t (RequestT t request response m b)
206+ -> RequestT t request response m (a, Event t b)
207+runWithReplaceRequestTWith f a0 a' = do
208+ response <- RequestT ask
209+ let g :: RequestT t request response m c -> m (c, [Event t (DMap (Tag (PrimState m)) request)])
210+ g (RequestT r) = runReaderT (runStateT r mempty) response
211+ (result0, result') <- f (g a0) $ g <$> a'
212+ request <- holdDyn (fmap (mconcat . NonEmpty.toList) $ mergeList $ snd result0) $ fmap (mconcat . NonEmpty.toList) . mergeList . snd <$> result'
213+ -- 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.)
214+ RequestT $ modify $ (:) $ coincidence $ updated request
215+ RequestT $ modify $ (:) $ switch $ current request
216+ return (fst result0, fst <$> result')
217+
218+sequenceDMapWithAdjustRequestTWith :: (GCompare k, Reflex t, MonadHold t m)
219+ => (forall k'. GCompare k'
220+ => DMap k' m
221+ -> Event t (PatchDMap k' m)
222+ -> RequestT t request response m (DMap k' Identity, Event t (PatchDMap k' Identity))
223+ )
224+ -> DMap k (RequestT t request response m)
225+ -> Event t (PatchDMap k (RequestT t request response m))
226+ -> RequestT t request response m (DMap k Identity, Event t (PatchDMap k Identity))
227+sequenceDMapWithAdjustRequestTWith f (dm0 :: DMap k (RequestT t request response m)) dm' = do
228+ response <- RequestT ask
229+ let inputTransform :: forall a. DMapTransform a k (WrapArg ((,) [Event t (DMap (Tag (PrimState m)) request)]) k) (RequestT t request response m) m
230+ inputTransform = DMapTransform WrapArg (\(RequestT v) -> swap <$> runReaderT (runStateT v mempty) response)
231+ (children0, children') <- f (mapKeysAndValuesMonotonic inputTransform dm0) $ mapPatchKeysAndValuesMonotonic inputTransform <$> dm'
232+ let result0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, v)) -> k :=> Identity v) children0
233+ result' = ffor children' $ \(PatchDMap p) -> PatchDMap $
234+ mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mv) -> k :=> ComposeMaybe (fmap (Identity . snd . runIdentity) mv)) p
235+ requests0 :: DMap (Const2 (Some k) (DMap (Tag (PrimState m)) request)) (Event t)
236+ requests0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (r, _)) -> Const2 (Some.This k) :=> mergeWith DMap.union r) children0
237+ requests' :: Event t (PatchDMap (Const2 (Some k) (DMap (Tag (PrimState m)) request)) (Event t))
238+ requests' = ffor children' $ \(PatchDMap p) -> PatchDMap $
239+ mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mv) -> Const2 (Some.This k) :=> ComposeMaybe (fmap (mergeWith DMap.union . fst . runIdentity) mv)) p
240+ childRequestMap <- holdIncremental requests0 requests'
241+ -- 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.)
242+ RequestT $ modify $ (:) $ coincidence $ ffor requests' $ \(PatchDMap p) -> mergeWith DMap.union $ catMaybes $ ffor (DMap.toList p) $ \(Const2 _ :=> ComposeMaybe me) -> me
243+ RequestT $ modify $ (:) $ ffor (mergeIncremental childRequestMap) $ \m ->
244+ mconcat $ (\(Const2 _ :=> Identity reqs) -> reqs) <$> DMap.toList m
245+ return (result0, result')
246+
247+instance PerformEvent t m => PerformEvent t (RequestT t request response m) where
248+ type Performable (RequestT t request response m) = Performable m
249+ performEvent_ = lift . performEvent_
250+ performEvent = lift . performEvent
251+
252+instance PostBuild t m => PostBuild t (RequestT t request response m) where
253+ getPostBuild = lift getPostBuild
254+
255+instance TriggerEvent t m => TriggerEvent t (RequestT t request response m) where
256+ newTriggerEvent = lift newTriggerEvent
257+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
258+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
259+
260+instance MonadReader r m => MonadReader r (RequestT t request response m) where
261+ ask = lift ask
262+ local f (RequestT a) = RequestT $ mapStateT (mapReaderT $ local f) a
263+ reader = lift . reader
264+
265+{-
266+withRequestT :: forall t m a (req :: * -> *) (req' :: * -> *).
267+ (Reflex t, MonadHold t m, MonadFix m)
268+ => (forall x. request x -> request' x)
269+ -> (forall x. response' x -> response x)
270+ -> RequestT t request response m a
271+ -> RequestT t request' response m a
272+withRequestT f g m = RequestT $ hoist (hoist (withDynamicWriterT f')) (unRequestT m)
273+ where
274+ f' :: Event t [(y, Some request)] -> Event t [(y, Some request')]
275+ f' = fmap . fmap . fmap $ \(SomeRequest req) -> SomeRequest $ f req
276+-}
277+
278+instance MonadRef m => MonadRef (RequestT t request response m) where
279+ type Ref (RequestT t request response m) = Ref m
280+ newRef = lift . newRef
281+ readRef = lift . readRef
282+ writeRef r = lift . writeRef r
283+
284+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequestT t request response m) where
285+ newEventWithTrigger = lift . newEventWithTrigger
286+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
287+
288+
289+--------------------------------------------------------------------------------
290+-- Helpers
291+--------------------------------------------------------------------------------
292+
293+{-# INLINABLE numberWith #-}
294+numberWith :: (Enum n, Traversable t) => (n -> a -> b) -> t a -> n -> (t b, n)
295+numberWith f t n0 = flip runState n0 $ forM t $ \x -> state $ \n -> (f n x, succ n)
296+
297+{-# INLINABLE mergeSelfDeletingEvents #-}
298+mergeSelfDeletingEvents :: forall t m k v. (Ord k, Reflex t, MonadFix m, MonadHold t m)
299+ => DMap (Const2 k (These v ())) (Event t)
300+ -> Event t (PatchDMap (Const2 k (These v ())) (Event t))
301+ -> m (Event t [v])
302+mergeSelfDeletingEvents initial eAddNew = do
303+ rec events :: Incremental t (PatchDMap (Const2 k (These v ())) (Event t))
304+ <- holdIncremental initial $ eAddNew <> eDelete --TODO: Eliminate empty firings
305+ let event = mergeWith DMap.union
306+ [ mergeIncremental events
307+ , coincidence $ ffor eAddNew $ \(PatchDMap m) -> merge $ DMap.mapMaybeWithKey (const getComposeMaybe) m
308+ ]
309+ eDelete :: Event t (PatchDMap (Const2 k (These v ())) (Event t))
310+ eDelete = fforMaybe event $
311+ fmap (PatchDMap . DMap.fromDistinctAscList) .
312+ (\l -> if null l then Nothing else Just l) .
313+ fmapMaybe (\(Const2 k :=> Identity x) -> (Const2 k :=> ComposeMaybe Nothing) <$ x ^? there) .
314+ DMap.toList
315+ return $ ffor event $
316+ fmapMaybe (\(Const2 _ :=> Identity x) -> x ^? here) .
317+ DMap.toList
src/Reflex/PerformEvent/Class.hs+47
@@ -7,11 +7,15 @@
77{-# LANGUAGE TypeFamilies #-}
88{-# LANGUAGE TypeOperators #-}
99{-# LANGUAGE UndecidableInstances #-}
10+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1011module Reflex.PerformEvent.Class where
1112
1213import Reflex.Class
1314
15+import Control.Monad.Identity
1416import Control.Monad.Reader
17+import qualified Control.Monad.State.Lazy as Lazy
18+import Control.Monad.State.Strict
1519
1620class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
1721 type Performable m :: * -> *
@@ -24,6 +28,11 @@ class Monad m => TriggerEvent t m | m -> t where
2428 newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
2529 newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
2630
31+class (Reflex t, Monad m) => MonadRequest t m | m -> t where
32+ type Request m :: * -> *
33+ type Response m :: * -> *
34+ withRequesting :: (Event t (Response m a) -> m (Event t (Request m a), r)) -> m r
35+
2736{-# INLINABLE performEventAsync #-}
2837performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
2938performEventAsync e = do
@@ -44,3 +53,41 @@ instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
4453 newTriggerEvent = lift newTriggerEvent
4554 newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
4655 newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
56+
57+instance MonadRequest t m => MonadRequest t (ReaderT r m) where
58+ type Request (ReaderT r m) = Request m
59+ type Response (ReaderT r m) = Response m
60+ withRequesting f = do
61+ r <- ask
62+ lift $ withRequesting $ (`runReaderT` r) . f
63+
64+instance MonadRequest t m => MonadRequest t (StateT s m) where
65+ type Request (StateT s m) = Request m
66+ type Response (StateT s m) = Response m
67+ withRequesting f = do
68+ old <- get
69+ (result, new) <- lift $ withRequesting $ \x -> do
70+ ((request, result), new) <- runStateT (f x) old
71+ return (request, (result, new))
72+ put new
73+ return result
74+
75+instance MonadRequest t m => MonadRequest t (Lazy.StateT s m) where
76+ type Request (Lazy.StateT s m) = Request m
77+ type Response (Lazy.StateT s m) = Response m
78+ withRequesting f = do
79+ old <- Lazy.get
80+ (result, new) <- lift $ withRequesting $ \x -> do
81+ ((request, result), new) <- Lazy.runStateT (f x) old
82+ return (request, (result, new))
83+ Lazy.put new
84+ return result
85+
86+requesting :: MonadRequest t m => Event t (Request m a) -> m (Event t (Response m a))
87+requesting req = withRequesting $ return . (,) req
88+
89+requesting_ :: MonadRequest t m => Event t (Request m a) -> m ()
90+requesting_ req = withRequesting $ \_ -> return (req, ())
91+
92+requestingIdentity :: (MonadRequest t m, Response m ~ Identity) => Event t (Request m a) -> m (Event t a)
93+requestingIdentity = fmap (fmap runIdentity) . requesting
src/Reflex/PostBuild/Class.hs+25−5
@@ -3,18 +3,21 @@
33{-# LANGUAGE FunctionalDependencies #-}
44{-# LANGUAGE GeneralizedNewtypeDeriving #-}
55{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RecursiveDo #-}
67{-# LANGUAGE ScopedTypeVariables #-}
78{-# LANGUAGE TypeFamilies #-}
89{-# LANGUAGE TypeOperators #-}
910{-# LANGUAGE UndecidableInstances #-}
11+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1012module Reflex.PostBuild.Class where
1113
1214import Control.Monad.Exception
15+import Control.Monad.Primitive
1316import Control.Monad.Reader
1417import Control.Monad.Ref
1518import Control.Monad.Trans.Control
19+import qualified Data.Dependent.Map as DMap
1620import Reflex.Class
17−import Reflex.Deletable.Class
1821import Reflex.Host.Class
1922import Reflex.PerformEvent.Class
2023
@@ -23,6 +26,10 @@ class (Reflex t, Monad m) => PostBuild t m | m -> t where
2326
2427newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
2528
29+instance PrimMonad m => PrimMonad (PostBuildT x m) where
30+ type PrimState (PostBuildT x m) = PrimState m
31+ primitive = lift . primitive
32+
2633instance MonadTransControl (PostBuildT t) where
2734 type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
2835 {-# INLINABLE liftWith #-}
@@ -34,10 +41,6 @@ instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
3441 {-# INLINABLE getPostBuild #-}
3542 getPostBuild = PostBuildT ask
3643
37−instance Deletable t m => Deletable t (PostBuildT t m) where
38− {-# INLINABLE deletable #-}
39− deletable = liftThrough . deletable
40−
4144instance MonadSample t m => MonadSample t (PostBuildT t m) where
4245 {-# INLINABLE sample #-}
4346 sample = lift . sample
@@ -89,3 +92,20 @@ runPostBuildT (PostBuildT a) = runReaderT a
8992
9093instance PostBuild t m => PostBuild t (ReaderT r m) where
9194 getPostBuild = lift getPostBuild
95+
96+instance (Reflex t, MonadHold t m, MonadFix m, MonadAdjust t m, PerformEvent t m) => MonadAdjust t (PostBuildT t m) where
97+ runWithReplace a0 a' = do
98+ postBuild <- getPostBuild
99+ lift $ do
100+ rec result@(_, result') <- runWithReplace (runPostBuildT a0 postBuild) $ fmap (\v -> runPostBuildT v =<< headE voidResult') a'
101+ let voidResult' = void result'
102+ return result
103+ sequenceDMapWithAdjust dm0 dm' = do
104+ postBuild <- getPostBuild
105+ let loweredDm0 = DMap.map (`runPostBuildT` postBuild) dm0
106+ lift $ do
107+ rec (result0, result') <- sequenceDMapWithAdjust loweredDm0 loweredDm'
108+ let voidResult' = void result'
109+ let loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
110+ DMap.map (ComposeMaybe . fmap (\v -> runPostBuildT v =<< headE voidResult') . getComposeMaybe) p --TODO: Avoid doing this headE so many times; once per loweredDm' firing ought to be OK, but it's not totally trivial to do because result' might be firing at the same time, and we don't want *that* to be the postBuild occurrence
111+ return (result0, result')
src/Reflex/Pure.hs+7−2
@@ -5,6 +5,7 @@
55{-# LANGUAGE MultiParamTypeClasses #-}
66{-# LANGUAGE ScopedTypeVariables #-}
77{-# LANGUAGE TypeFamilies #-}
8+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
89
910-- There are two expected orphan instances in this module:
1011-- * MonadSample (Pure t) ((->) t)
@@ -28,6 +29,7 @@ import Data.Functor.Identity
2829import Data.Maybe
2930import Data.MemoTrie
3031import Data.Monoid
32+import Data.Type.Coercion
3133import Reflex.Class
3234
3335data Pure t
@@ -40,7 +42,7 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
4042 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
4143 newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
4244 newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
43− newtype Incremental (Pure t) p a = Incremental { unIncremental :: t -> (a, Maybe (p a)) }
45+ newtype Incremental (Pure t) p = Incremental { unIncremental :: t -> (PatchTarget p, Maybe p) }
4446
4547 type PushM (Pure t) = (->) t
4648 type PullM (Pure t) = (->) t
@@ -109,6 +111,9 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
109111 Nothing -> Nothing
110112 Just patch -> apply patch old
111113 in (old, e)
114+ behaviorCoercion Coercion = Coercion
115+ eventCoercion Coercion = Coercion
116+ dynamicCoercion Coercion = Coercion
112117
113118instance Functor (Dynamic (Pure t)) where
114119 fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
@@ -152,7 +157,7 @@ instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
152157 let Behavior f = hold initialValue e initialTime
153158 in Dynamic $ \t -> (f t, unEvent e t)
154159
155− holdIncremental :: Patch p => a -> Event (Pure t) (p a) -> t -> Incremental (Pure t) p a
160+ holdIncremental :: Patch p => PatchTarget p -> Event (Pure t) p -> t -> Incremental (Pure t) p
156161 holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
157162 where f = memo $ \sampleTime ->
158163 -- Really, the sampleTime should never be prior to the initialTime,
src/Reflex/Spider.hs+4−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- | This module exports all of the user-facing functionality of the 'Spider'
23-- 'Reflex' engine
34module Reflex.Spider
@@ -9,9 +10,11 @@ module Reflex.Spider
910 , runSpiderHostForTimeline
1011 , newSpiderTimeline
1112 , withSpiderTimeline
12− , askSpiderTimeline
1313 -- * Deprecated
1414 , SpiderEnv
1515 ) where
1616
17+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
18+import Reflex.Class
19+#endif
1720import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hs+1000−1260
@@ -2,7 +2,6 @@
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE ConstraintKinds #-}
44{-# LANGUAGE EmptyDataDecls #-}
5−{-# LANGUAGE ExistentialQuantification #-}
65{-# LANGUAGE FlexibleContexts #-}
76{-# LANGUAGE FlexibleInstances #-}
87{-# LANGUAGE FunctionalDependencies #-}
@@ -18,6 +17,10 @@
1817{-# LANGUAGE TypeFamilies #-}
1918{-# LANGUAGE TypeOperators #-}
2019{-# LANGUAGE UndecidableInstances #-}
20+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
21+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
22+{-# OPTIONS_GHC -fno-warn-orphans #-}
23+#endif
2124-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
2225-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2326module Reflex.Spider.Internal where
@@ -31,46 +34,57 @@ import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
3134import Control.Monad.Primitive
3235import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
3336import Control.Monad.Ref
34−import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
3537import Data.Align
3638import Data.Coerce
3739import Data.Dependent.Map (DMap, DSum (..))
3840import qualified Data.Dependent.Map as DMap
3941import Data.Foldable hiding (concat, elem, sequence_)
4042import Data.Function
41−import Data.Functor.Compose
43+import Data.Functor.Constant
44+import Data.Functor.Misc
45+import Data.Functor.Product
4246import Data.GADT.Compare
4347import Data.IntMap.Strict (IntMap)
4448import qualified Data.IntMap.Strict as IntMap
4549import Data.IORef
4650import Data.List hiding (foldl')
47−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
48−import qualified Data.List.NonEmpty as NonEmpty
4951import Data.Maybe
50−import Data.Monoid (mempty, (<>))
52+import Data.Monoid ((<>))
53+import Data.Proxy
5154import Data.Some (Some)
5255import qualified Data.Some as Some
5356import Data.These
5457import Data.Traversable
55−import Data.Tree (Forest, Tree (..), drawForest)
5658import Data.Word
59+import GHC.Exts
5760import GHC.IORef (IORef (..))
5861import GHC.Stack
62+import Reflex.FunctorMaybe
5963import System.IO.Unsafe
6064import System.Mem.Weak
6165import Unsafe.Coerce
6266
67+#ifdef DEBUG_CYCLES
68+import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
69+import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
70+import qualified Data.List.NonEmpty as NonEmpty
71+import Data.Monoid (mempty)
72+import Data.Tree (Forest, Tree (..), drawForest)
73+#endif
74+
6375import Data.WeakBag (WeakBag, WeakBagTicket)
6476import qualified Data.WeakBag as WeakBag
77+#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
78+import Data.Type.Coercion
79+import qualified Reflex.Class
6580import qualified Reflex.Class as R
66−import qualified Reflex.Host.Class as R
81+import qualified Reflex.Host.Class
82+#endif
83+import Reflex.Patch
6784
6885-- Note: must come last to silence warnings due to AMP on GHC < 7.10
6986import Prelude hiding (any, concat, mapM, mapM_, sequence)
7087
71−type SubscriberList a = WeakBag (Subscriber a)
72−type SubscriberListTicket a = WeakBagTicket (Subscriber a)
73−
7488debugPropagate :: Bool
7589
7690debugInvalidateHeight :: Bool
@@ -90,35 +104,32 @@ debugInvalidate = True
90104class HasNodeId a where
91105 getNodeId :: a -> Int
92106
93−instance HasNodeId (Hold p a) where
107+instance HasNodeId (Hold x p a) where
94108 getNodeId = holdNodeId
95109
96−instance HasNodeId (PushSubscribed a b) where
110+instance HasNodeId (PushSubscribed x a b) where
97111 getNodeId = pushSubscribedNodeId
98112
99−instance HasNodeId (SwitchSubscribed a) where
113+instance HasNodeId (SwitchSubscribed x a) where
100114 getNodeId = switchSubscribedNodeId
101115
102−instance HasNodeId (MergeSubscribed a) where
103− getNodeId = mergeSubscribedNodeId
104−
105−instance HasNodeId (FanSubscribed a) where
116+instance HasNodeId (FanSubscribed x a) where
106117 getNodeId = fanSubscribedNodeId
107118
108−instance HasNodeId (CoincidenceSubscribed a) where
119+instance HasNodeId (CoincidenceSubscribed x a) where
109120 getNodeId = coincidenceSubscribedNodeId
110121
111−instance HasNodeId (RootSubscribed a) where
122+instance HasNodeId (RootSubscribed x a) where
112123 getNodeId = rootSubscribedNodeId
113124
114−instance HasNodeId (Pull a) where
125+instance HasNodeId (Pull x a) where
115126 getNodeId = pullNodeId
116127
117128{-# INLINE showNodeId #-}
118129showNodeId :: HasNodeId a => a -> String
119130showNodeId = ("#"<>) . show . getNodeId
120131
121−showSubscriberType :: Subscriber a -> String
132+showSubscriberType :: Subscriber x a -> String
122133showSubscriberType = \case
123134 SubscriberPush _ _ -> "SubscriberPush"
124135 SubscriberMerge _ _ -> "SubscriberMerge"
@@ -131,7 +142,7 @@ showSubscriberType = \case
131142 SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
132143 SubscriberHandle -> "SubscriberHandle"
133144
134−showEventType :: Event a -> String
145+showEventType :: Event x a -> String
135146showEventType = \case
136147 EventRoot _ _ -> "EventRoot"
137148 EventNever -> "EventNever"
@@ -179,153 +190,359 @@ unsafeNodeId a = unsafePerformIO $ do
179190-- Event
180191--------------------------------------------------------------------------------
181192
182−data Event a
183− = forall k. GCompare k => EventRoot !(k a) !(Root k)
184− | EventNever
185− | forall b. EventPush !(Push b a)
186− | forall k. (GCompare k, a ~ DMap k Identity) => EventMerge !(Merge k)
187− | forall k. GCompare k => EventFan !(k a) !(Fan k)
188− | EventSwitch !(Switch a)
189− | EventCoincidence !(Coincidence a)
190− | forall p b. (a ~ p b, R.Patch p) => EventHold !(Hold p b)
191− | forall p b. (a ~ p b, R.Patch p) => EventDyn !(Dyn p b)
192− | forall b. (a ~ Identity b) => EventHoldIdentity !(Hold Identity b)
193− | forall b. (a ~ Identity b) => EventDynIdentity !(Dyn Identity b)
194−
195−{-# SPECIALIZE subscribe :: Event a -> Subscriber a -> EventM x (EventSubscription a) #-}
196−subscribe :: forall x m a. CanSubscribe x m => Event a -> Subscriber a -> m (EventSubscription a)
197−subscribe e sub = case e of
198− EventRoot k r -> wrap EventSubscribedRoot $ liftIO . getRootSubscribed k r
199− EventNever -> return $ EventSubscription Nothing EventSubscribedNever
200− EventPush p -> wrap EventSubscribedPush $ getPushSubscribed p
201− EventFan k f -> wrap (EventSubscribedFan k) $ getFanSubscribed k f
202− EventMerge m -> wrap EventSubscribedMerge $ getMergeSubscribed m
203− EventSwitch s -> wrap EventSubscribedSwitch $ getSwitchSubscribed s
204− EventCoincidence c -> wrap EventSubscribedCoincidence $ getCoincidenceSubscribed c
205− EventHold h -> subscribeHoldEvent h sub
206− EventDyn j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
207− EventHoldIdentity h -> subscribeHoldEvent h sub
208− EventDynIdentity j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
209− where
210− wrap :: (t -> EventSubscribed a) -> (Subscriber a -> m (SubscriberListTicket a, t)) -> m (EventSubscription a)
211− wrap tag getSpecificSubscribed = do
212− (sln, subd) <- getSpecificSubscribed sub
213− return $ EventSubscription (Just sln) $ tag subd
193+newtype Event x a = Event { unEvent :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
194+
195+{-# INLINE subscribeAndRead #-}
196+subscribeAndRead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
197+subscribeAndRead = unEvent
198+
199+{-# RULES
200+"cacheEvent/cacheEvent" forall e. cacheEvent (cacheEvent e) = cacheEvent e
201+"cacheEvent/pushCheap" forall f e. pushCheap f (cacheEvent e) = cacheEvent (pushCheap f e)
202+"hold/cacheEvent" forall f e. hold f (cacheEvent e) = hold f e
203+ #-}
204+{- RULES
205+ "subscribeAndRead/cacheEvent" forall e. subscribeAndRead (cacheEvent e) = subscribeAndRead e
206+ #-}
207+
208+-- | Construct an 'Event' equivalent to that constructed by 'push', but with no
209+-- caching; if the computation function is very cheap, this is (much) more
210+-- efficient than 'push'
211+{- INLINE [1] pushCheap #-}
212+{-# NOINLINE pushCheap #-}
213+pushCheap :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
214+pushCheap f e = Event $ \sub -> do
215+ (subscription, occ) <- subscribeAndRead e $ sub
216+ { subscriberPropagate = \a -> do
217+ mb <- runComputeM $ f a
218+ mapM_ (subscriberPropagate sub) mb
219+ }
220+ occ' <- join <$> mapM (runComputeM . f) occ
221+ return (subscription, occ')
222+
223+data CacheSubscribed x a
224+ = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(WeakBag (Subscriber x a))
225+ , _cacheSubscribed_weakSelf :: {-# UNPACK #-} !(IORef (Weak (CacheSubscribed x a)))
226+ , _cacheSubscribed_parent :: {-# UNPACK #-} !(EventSubscription x)
227+ , _cacheSubscribed_cached :: {-# UNPACK #-} !(IORef (Maybe (CacheSubscribed x a)))
228+ , _cacheSubscribed_occurrence :: {-# UNPACK #-} !(IORef (Maybe a))
229+ }
230+
231+-- | Construct an 'Event' whose value is guaranteed not to be recomputed
232+-- repeatedly
233+--
234+--TODO: Try a caching strategy where we subscribe directly to the parent when
235+--there's only one subscriber, and then build our own WeakBag only when a second
236+--subscriber joins
237+{-# NOINLINE cacheEvent #-}
238+cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
239+cacheEvent e = Event $ \sub -> {-# SCC "cacheEvent" #-} do
240+ let !mSubscribedRef = unsafeNewIORef e Nothing
241+ mSubscribed <- liftIO $ readIORef mSubscribedRef
242+ case mSubscribed of
243+ Just subscribed -> liftIO $ do
244+ ticket <- WeakBag.insert sub (_cacheSubscribed_subscribers subscribed) (_cacheSubscribed_weakSelf subscribed) cleanupCacheSubscribed
245+ occ <- readIORef $ _cacheSubscribed_occurrence subscribed
246+ let es = EventSubscribed
247+ { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
248+ , eventSubscribedRetained = toAny subscribed
249+ }
250+ return (EventSubscription (WeakBag.remove ticket >> touch ticket) es, occ)
251+ Nothing -> do
252+ weakSelf <- liftIO $ newIORef $ error "cacheEvent: weakSelf not yet intialized"
253+ (subscribers, ticket) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCacheSubscribed
254+ occRef <- liftIO $ newIORef undefined
255+ (parentSub, occ) <- subscribeAndRead e $ Subscriber
256+ { subscriberPropagate = \a -> do
257+ liftIO $ writeIORef occRef $ Just a
258+ scheduleClear occRef
259+ propagate a subscribers
260+ , subscriberInvalidateHeight = \old -> do
261+ WeakBag.traverse subscribers $ invalidateSubscriberHeight old
262+ , subscriberRecalculateHeight = \new -> do
263+ WeakBag.traverse subscribers $ recalculateSubscriberHeight new
264+ }
265+ liftIO $ writeIORef occRef occ
266+ when (isJust occ) $ scheduleClear occRef
267+ let !subscribed = CacheSubscribed
268+ { _cacheSubscribed_subscribers = subscribers
269+ , _cacheSubscribed_weakSelf = weakSelf
270+ , _cacheSubscribed_parent = parentSub
271+ , _cacheSubscribed_cached = mSubscribedRef
272+ , _cacheSubscribed_occurrence = occRef
273+ }
274+ liftIO $ writeIORef mSubscribedRef $ Just subscribed
275+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
276+ return (EventSubscription (WeakBag.remove ticket >> touch ticket) (EventSubscribed (eventSubscribedHeightRef $ _eventSubscription_subscribed parentSub) (toAny subscribed)), occ)
277+
278+cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
279+cleanupCacheSubscribed self = do
280+ unsubscribe $ _cacheSubscribed_parent self
281+ writeIORef (_cacheSubscribed_cached self) Nothing
282+
283+subscribe :: Event x a -> Subscriber x a -> EventM x (EventSubscription x)
284+subscribe e s = fst <$> subscribeAndRead e s
285+
286+{-# INLINE wrap #-}
287+wrap :: MonadIO m => (t -> EventSubscribed x) -> (Subscriber x a -> m (WeakBagTicket, t, Maybe a)) -> Subscriber x a -> m (EventSubscription x, Maybe a)
288+wrap tag getSpecificSubscribed sub = do
289+ (sln, subd, occ) <- getSpecificSubscribed sub
290+ let es = tag subd
291+ return (EventSubscription (WeakBag.remove sln >> touch sln) es, occ)
292+
293+eventRoot :: GCompare k => k a -> Root x k -> Event x a
294+eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed k r
295+
296+eventNever :: Event x a
297+eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
298+
299+eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
300+eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
301+
302+eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
303+eventSwitch !s = Event $ wrap eventSubscribedSwitch $ getSwitchSubscribed s
304+
305+eventCoincidence :: HasSpiderTimeline x => Coincidence x a -> Event x a
306+eventCoincidence !c = Event $ wrap eventSubscribedCoincidence $ getCoincidenceSubscribed c
307+
308+eventHold :: HasSpiderTimeline x => Hold x p -> Event x p
309+eventHold !h = Event $ subscribeHoldEvent h
310+
311+eventDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Event x p
312+eventDyn !j = Event $ \sub -> getDynHold j >>= \h -> subscribeHoldEvent h sub
214313
215314{-# INLINE subscribeCoincidenceInner #-}
216−subscribeCoincidenceInner :: CanSubscribe x m => Event a -> Height -> CoincidenceSubscribed a -> m (Maybe a, Height, EventSubscribed a)
315+subscribeCoincidenceInner :: CanSubscribe x m => Event x a -> Height -> CoincidenceSubscribed x a -> m (Maybe a, Height, EventSubscribed x)
217316subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
218317 subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
219− subscription@(EventSubscription _ innerSubd) <- subscribe inner subInner
220− innerOcc <- liftIO $ readEventSubscribed innerSubd
318+ (subscription@(EventSubscription _ innerSubd), innerOcc) <- subscribeAndRead inner subInner
221319 innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
222320 let height = max innerHeight outerHeight
223321 defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
224322 return (innerOcc, height, innerSubd)
225323
324+--------------------------------------------------------------------------------
325+-- Subscriber
326+--------------------------------------------------------------------------------
327+
328+data Subscriber x a = Subscriber
329+ { subscriberPropagate :: !(a -> EventM x ())
330+ , subscriberInvalidateHeight :: !(Height -> IO ())
331+ , subscriberRecalculateHeight :: !(Height -> IO ())
332+ }
333+
334+--TODO: Move this comment to WeakBag
335+-- These function are constructor functions that are marked NOINLINE so they are
336+-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
337+-- so any weak references that are attached to the constructors will have their
338+-- finalizer run. Using the opaque constructor, does not see the
339+-- constructor application, so it behaves like an IORef and cannot be fused away.
340+--
341+-- The result is also evaluated to WHNF, since forcing a thunk invalidates
342+-- the weak pointer to it in some cases.
343+
344+newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
345+newSubscriberHold h = return $ Subscriber
346+ { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
347+ , subscriberInvalidateHeight = \_ -> return ()
348+ , subscriberRecalculateHeight = \_ -> return ()
349+ }
350+
351+newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
352+newSubscriberFan subscribed = return $ Subscriber
353+ { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
354+ subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
355+ tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
356+ liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
357+ scheduleClear $ fanSubscribedOccurrence subscribed
358+ let f _ (Pair (Identity v) subsubs) = do
359+ propagate v $ _fanSubscribedChildren_list subsubs
360+ return $ Constant ()
361+ _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
362+ return ()
363+ , subscriberInvalidateHeight = \old -> do
364+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
365+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
366+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
367+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
368+ , subscriberRecalculateHeight = \new -> do
369+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
370+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
371+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
372+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
373+ }
374+
375+newSubscriberSwitch :: forall x a. HasSpiderTimeline x => SwitchSubscribed x a -> IO (Subscriber x a)
376+newSubscriberSwitch subscribed = return $ Subscriber
377+ { subscriberPropagate = \a -> {-# SCC "traverseSwitch" #-} do
378+ tracePropagate (Proxy :: Proxy x) $ "SubscriberSwitch" <> showNodeId subscribed
379+ liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
380+ scheduleClear $ switchSubscribedOccurrence subscribed
381+ propagate a $ switchSubscribedSubscribers subscribed
382+ , subscriberInvalidateHeight = \_ -> do
383+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
384+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
385+ when (oldHeight /= invalidHeight) $ do
386+ writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
387+ WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
388+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
389+ , subscriberRecalculateHeight = \new -> do
390+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
391+ updateSwitchHeight new subscribed
392+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
393+ }
394+
395+newSubscriberCoincidenceOuter :: forall x b. HasSpiderTimeline x => CoincidenceSubscribed x b -> IO (Subscriber x (Event x b))
396+newSubscriberCoincidenceOuter subscribed = return $ Subscriber
397+ { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceOuter" #-} do
398+ tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
399+ outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
400+ tracePropagate (Proxy :: Proxy x) $ " outerHeight = " <> show outerHeight
401+ (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
402+ tracePropagate (Proxy :: Proxy x) $ " isJust occ = " <> show (isJust occ)
403+ tracePropagate (Proxy :: Proxy x) $ " innerHeight = " <> show innerHeight
404+ liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
405+ scheduleClear $ coincidenceSubscribedInnerParent subscribed
406+ case occ of
407+ Nothing -> do
408+ when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
409+ writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
410+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
411+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
412+ Just o -> do -- Since it's already firing, no need to adjust height
413+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
414+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
415+ propagate o $ coincidenceSubscribedSubscribers subscribed
416+ , subscriberInvalidateHeight = \_ -> do
417+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
418+ invalidateCoincidenceHeight subscribed
419+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
420+ , subscriberRecalculateHeight = \_ -> do
421+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
422+ recalculateCoincidenceHeight subscribed
423+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
424+ }
425+
426+newSubscriberCoincidenceInner :: forall x a. HasSpiderTimeline x => CoincidenceSubscribed x a -> IO (Subscriber x a)
427+newSubscriberCoincidenceInner subscribed = return $ Subscriber
428+ { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceInner" #-} do
429+ tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceInner" <> showNodeId subscribed
430+ occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
431+ case occ of
432+ Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
433+ Nothing -> do
434+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
435+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
436+ propagate a $ coincidenceSubscribedSubscribers subscribed
437+ , subscriberInvalidateHeight = \_ -> do
438+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
439+ invalidateCoincidenceHeight subscribed
440+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
441+ , subscriberRecalculateHeight = \_ -> do
442+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
443+ recalculateCoincidenceHeight subscribed
444+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
445+ }
446+
447+invalidateSubscriberHeight :: Height -> Subscriber x a -> IO ()
448+invalidateSubscriberHeight = flip subscriberInvalidateHeight
449+
450+recalculateSubscriberHeight :: Height -> Subscriber x a -> IO ()
451+recalculateSubscriberHeight = flip subscriberRecalculateHeight
452+
453+-- | Propagate everything at the current height
454+propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
455+propagate a subscribers = withIncreasedDepth $ do
456+ -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
457+ --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
458+ WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
459+
226460--------------------------------------------------------------------------------
227461-- EventSubscribed
228462--------------------------------------------------------------------------------
229463
230−data EventSubscribed a
231− = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
232− | EventSubscribedNever
233− | forall b. EventSubscribedPush !(PushSubscribed b a)
234− | forall k. (GCompare k, a ~ DMap k Identity) => EventSubscribedMerge !(MergeSubscribed k)
235− | forall k. GCompare k => EventSubscribedFan !(k a) !(FanSubscribed k)
236− | EventSubscribedSwitch !(SwitchSubscribed a)
237− | EventSubscribedCoincidence !(CoincidenceSubscribed a)
238−
239−readEventSubscribed :: EventSubscribed a -> IO (Maybe a)
240−readEventSubscribed = \case
241− EventSubscribedRoot rs -> readRootSubscribed rs
242− EventSubscribedNever -> return Nothing
243− EventSubscribedPush ps -> readPushSubscribed ps
244− EventSubscribedMerge ms -> readMergeSubscribed ms
245− EventSubscribedFan k fs -> readFanSubscribed k fs
246− EventSubscribedSwitch ss -> readSwitchSubscribed ss
247− EventSubscribedCoincidence cs -> readCoincidenceSubscribed cs
248−
249−{-# INLINE subscribeEventSubscribed #-}
250−subscribeEventSubscribed :: EventSubscribed a -> Subscriber a -> IO (Maybe (SubscriberListTicket a))
251−subscribeEventSubscribed es sub = case es of
252− EventSubscribedRoot r -> do
253− traceSubscribe $ "subscribeEventSubscribed Root"
254− Just <$> subscribeRootSubscribed r sub
255− EventSubscribedNever -> do
256− traceSubscribe $ "subscribeEventSubscribed Never"
257− return Nothing
258− EventSubscribedPush subscribed -> do
259− traceSubscribe $ "subscribeEventSubscribed Push"
260− Just <$> subscribePushSubscribed subscribed sub
261− EventSubscribedFan k subscribed -> do
262− traceSubscribe $ "subscribeEventSubscribed Fan"
263− Just <$> subscribeFanSubscribed k subscribed sub
264− EventSubscribedMerge subscribed -> do
265− traceSubscribe $ "subscribeEventSubscribed Merge"
266− Just <$> subscribeMergeSubscribed subscribed sub
267− EventSubscribedSwitch subscribed -> do
268− traceSubscribe $ "subscribeEventSubscribed Switch"
269− Just <$> subscribeSwitchSubscribed subscribed sub
270− EventSubscribedCoincidence subscribed -> do
271− traceSubscribe $ "subscribeEventSubscribed Coincidence"
272− Just <$> subscribeCoincidenceSubscribed subscribed sub
273−
274−{-# INLINE eventSubscribedGetParents #-}
275−eventSubscribedGetParents :: EventSubscribed a -> IO [Some EventSubscribed]
276−eventSubscribedGetParents = \case
277− EventSubscribedRoot _ -> return []
278− EventSubscribedNever -> return []
279− EventSubscribedPush subscribed -> return [Some.This $ _eventSubscription_subscribed $ pushSubscribedParent subscribed]
280− EventSubscribedFan _ subscribed -> return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
281− EventSubscribedMerge subscribed -> do
282− ps <- readIORef $ mergeSubscribedParents subscribed
283− return $ R.ffor (DMap.toList ps) $ \(_ :=> v) -> Some.This $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription v
284− EventSubscribedSwitch subscribed -> do
285− s <- readIORef $ switchSubscribedCurrentParent subscribed
286− return [Some.This $ _eventSubscription_subscribed s]
287− EventSubscribedCoincidence subscribed -> do
288− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
289− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
290− innerParents = maybeToList $ fmap Some.This innerSubscription
291− return $ outerParent : innerParents
292−
293−getEventSubscribedHeight :: EventSubscribed a -> IO Height
464+toAny :: a -> Any
465+toAny = unsafeCoerce
466+
467+data EventSubscribed x = EventSubscribed
468+ { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
469+ , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
470+#ifdef DEBUG_CYCLES
471+ , eventSubscribedGetParents :: !(IO [Some (EventSubscribed x)]) -- For debugging loops
472+ , eventSubscribedHasOwnHeightRef :: !Bool
473+ , eventSubscribedWhoCreated :: !(IO [String])
474+#endif
475+ }
476+
477+eventSubscribedRoot :: RootSubscribed x a -> EventSubscribed x
478+eventSubscribedRoot !r = EventSubscribed
479+ { eventSubscribedHeightRef = zeroRef
480+ , eventSubscribedRetained = toAny r
481+#ifdef DEBUG_CYCLES
482+ , eventSubscribedGetParents = return []
483+ , eventSubscribedHasOwnHeightRef = False
484+ , eventSubscribedWhoCreated = return ["root"]
485+#endif
486+ }
487+
488+eventSubscribedNever :: EventSubscribed x
489+eventSubscribedNever = EventSubscribed
490+ { eventSubscribedHeightRef = zeroRef
491+ , eventSubscribedRetained = toAny ()
492+#ifdef DEBUG_CYCLES
493+ , eventSubscribedGetParents = return []
494+ , eventSubscribedHasOwnHeightRef = False
495+ , eventSubscribedWhoCreated = return ["never"]
496+#endif
497+ }
498+
499+eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
500+eventSubscribedFan !subscribed = EventSubscribed
501+ { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
502+ , eventSubscribedRetained = toAny subscribed
503+#ifdef DEBUG_CYCLES
504+ , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
505+ , eventSubscribedHasOwnHeightRef = False
506+ , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
507+#endif
508+ }
509+
510+eventSubscribedSwitch :: SwitchSubscribed x a -> EventSubscribed x
511+eventSubscribedSwitch !subscribed = EventSubscribed
512+ { eventSubscribedHeightRef = switchSubscribedHeight subscribed
513+ , eventSubscribedRetained = toAny subscribed
514+#ifdef DEBUG_CYCLES
515+ , eventSubscribedGetParents = do
516+ s <- readIORef $ switchSubscribedCurrentParent subscribed
517+ return [Some.This $ _eventSubscription_subscribed s]
518+ , eventSubscribedHasOwnHeightRef = True
519+ , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
520+#endif
521+ }
522+
523+eventSubscribedCoincidence :: CoincidenceSubscribed x a -> EventSubscribed x
524+eventSubscribedCoincidence !subscribed = EventSubscribed
525+ { eventSubscribedHeightRef = coincidenceSubscribedHeight subscribed
526+ , eventSubscribedRetained = toAny subscribed
527+#ifdef DEBUG_CYCLES
528+ , eventSubscribedGetParents = do
529+ innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
530+ let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
531+ innerParents = maybeToList $ fmap Some.This innerSubscription
532+ return $ outerParent : innerParents
533+ , eventSubscribedHasOwnHeightRef = True
534+ , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
535+#endif
536+ }
537+
538+getEventSubscribedHeight :: EventSubscribed x -> IO Height
294539getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
295540
296−{-# INLINE eventSubscribedHeightRef #-}
297−eventSubscribedHeightRef :: EventSubscribed a -> IORef Height
298−eventSubscribedHeightRef = \case
299− EventSubscribedRoot _ -> zeroRef
300− EventSubscribedNever -> zeroRef
301− EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
302− EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
303− EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
304− EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
305− EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
306−
307−whoCreatedEventSubscribed :: EventSubscribed a -> IO [String]
308−whoCreatedEventSubscribed = \case
309− EventSubscribedRoot _ -> return ["root"]
310− EventSubscribedNever -> return ["never"]
311− EventSubscribedPush subscribed -> whoCreatedIORef $ pushSubscribedCachedSubscribed subscribed
312− EventSubscribedFan _ subscribed -> whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
313− EventSubscribedMerge subscribed -> whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
314− EventSubscribedSwitch subscribed -> whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
315− EventSubscribedCoincidence subscribed -> whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
316−
317−{-# INLINE eventSubscribedHasOwnHeightRef #-}
318−eventSubscribedHasOwnHeightRef :: EventSubscribed a -> Bool
319−eventSubscribedHasOwnHeightRef = \case
320− EventSubscribedRoot _ -> False
321− EventSubscribedNever -> False
322− EventSubscribedPush _ -> False
323− EventSubscribedFan _ _ -> False
324− EventSubscribedMerge _ -> True
325− EventSubscribedSwitch _ -> True
326− EventSubscribedCoincidence _ -> True
327−
328−walkInvalidHeightParents :: EventSubscribed a -> IO [Some EventSubscribed]
541+#ifdef DEBUG_CYCLES
542+whoCreatedEventSubscribed :: EventSubscribed x -> IO [String]
543+whoCreatedEventSubscribed = eventSubscribedWhoCreated
544+
545+walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
329546walkInvalidHeightParents s0 = do
330547 subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
331548 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
@@ -335,18 +552,115 @@ walkInvalidHeightParents s0 = do
335552 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
336553 forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
337554 return subscribers
555+#endif
556+
557+{-# INLINE subscribeHoldEvent #-}
558+subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
559+subscribeHoldEvent = subscribeAndRead . holdEvent
338560
339561--------------------------------------------------------------------------------
340562-- EventSubscription
341563--------------------------------------------------------------------------------
342564
343−data EventSubscription a = EventSubscription
344− { _eventSubscription_ticket :: !(Maybe (SubscriberListTicket a))
345− , _eventSubscription_subscribed :: !(EventSubscribed a)
565+data EventSubscription x = EventSubscription
566+ { _eventSubscription_unsubscribe :: !(IO ())
567+ , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
346568 }
347569
348−unsubscribe :: EventSubscription a -> IO ()
349−unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
570+unsubscribe :: EventSubscription x -> IO ()
571+unsubscribe (EventSubscription u _) = u
572+
573+--------------------------------------------------------------------------------
574+-- Behavior
575+--------------------------------------------------------------------------------
576+
577+newtype Behavior x a = Behavior { readBehaviorTracked :: BehaviorM x a }
578+
579+behaviorHold :: Hold x p -> Behavior x (PatchTarget p)
580+behaviorHold !h = Behavior $ readHoldTracked h
581+
582+behaviorHoldIdentity :: Hold x (Identity a) -> Behavior x a
583+behaviorHoldIdentity = behaviorHold
584+
585+behaviorConst :: a -> Behavior x a
586+behaviorConst !a = Behavior $ return a
587+
588+behaviorPull :: Pull x a -> Behavior x a
589+behaviorPull !p = Behavior $ do
590+ val <- liftIO $ readIORef $ pullValue p
591+ case val of
592+ Just subscribed -> do
593+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
594+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
595+ liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
596+ return $ pullSubscribedValue subscribed
597+ Nothing -> do
598+ i <- liftIO $ newInvalidatorPull p
599+ wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
600+ parentsRef <- liftIO $ newIORef []
601+ holdInits <- askBehaviorHoldInits
602+ a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
603+ invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
604+ parents <- liftIO $ readIORef parentsRef
605+ let subscribed = PullSubscribed
606+ { pullSubscribedValue = a
607+ , pullSubscribedInvalidators = invsRef
608+ , pullSubscribedOwnInvalidator = i
609+ , pullSubscribedParents = parents
610+ }
611+ liftIO $ writeIORef (pullValue p) $ Just subscribed
612+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
613+ return a
614+
615+behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
616+behaviorDyn !d = Behavior $ readHoldTracked =<< getDynHold d
617+
618+{-# INLINE readHoldTracked #-}
619+readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
620+readHoldTracked h = do
621+ result <- liftIO $ readIORef $ holdValue h
622+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
623+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
624+ return result
625+
626+{-# INLINABLE readBehaviorUntracked #-}
627+readBehaviorUntracked :: Defer (SomeHoldInit x) m => Behavior x a -> m a
628+readBehaviorUntracked b = do
629+ holdInits <- getDeferralQueue
630+ liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
631+
632+--------------------------------------------------------------------------------
633+-- Dynamic
634+--------------------------------------------------------------------------------
635+
636+data Dynamic x p = Dynamic
637+ { dynamicCurrent :: !(Behavior x (PatchTarget p))
638+ , dynamicUpdated :: !(Event x p)
639+ }
640+
641+dynamicHold :: HasSpiderTimeline x => Hold x p -> Dynamic x p
642+dynamicHold !h = Dynamic
643+ { dynamicCurrent = behaviorHold h
644+ , dynamicUpdated = eventHold h
645+ }
646+
647+dynamicHoldIdentity :: HasSpiderTimeline x => Hold x (Identity a) -> Dynamic x (Identity a)
648+dynamicHoldIdentity = dynamicHold
649+
650+dynamicConst :: PatchTarget p -> Dynamic x p
651+dynamicConst !a = Dynamic
652+ { dynamicCurrent = behaviorConst a
653+ , dynamicUpdated = eventNever
654+ }
655+
656+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
657+dynamicDyn !d = Dynamic
658+ { dynamicCurrent = behaviorDyn d
659+ , dynamicUpdated = eventDyn d
660+ }
661+
662+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
663+dynamicDynIdentity = dynamicDyn
350664
351665--------------------------------------------------------------------------------
352666-- Combinators
@@ -356,31 +670,56 @@ unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
356670--representational so we can use coerce
357671
358672--type role Hold representational
359−data Hold p a
360− = Hold { holdValue :: !(IORef a)
361− , holdInvalidators :: !(IORef [Weak Invalidator])
362− , holdParent :: !(IORef (Either (Event (p a)) (EventSubscription (p a)))) -- Keeps its parent alive (will be undefined until the hold is initialized) --TODO: Probably shouldn't be an IORef
673+data Hold x p
674+ = Hold { holdValue :: !(IORef (PatchTarget p))
675+ , holdInvalidators :: !(IORef [Weak (Invalidator x)])
676+ , holdEvent :: Event x p -- This must be lazy, or holds cannot be defined before their input Events
677+ , holdParent :: !(IORef (Maybe (EventSubscription x))) -- Keeps its parent alive (will be undefined until the hold is initialized) --TODO: Probably shouldn't be an IORef
363678#ifdef DEBUG_NODEIDS
364679 , holdNodeId :: Int
365680#endif
366681 }
367682
683+data Global
684+
685+{-# NOINLINE globalSpiderTimelineEnv #-}
686+globalSpiderTimelineEnv :: SpiderTimelineEnv Global
687+!globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
688+
689+-- | Stores all global data relevant to a particular Spider timeline; only one
690+-- value should exist for each type @x@
691+data SpiderTimelineEnv x = SpiderTimelineEnv
692+ { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
693+ , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
694+#ifdef DEBUG
695+ , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
696+#endif
697+ }
698+type role SpiderTimelineEnv nominal
699+
700+instance GEq SpiderTimelineEnv where
701+ a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
702+ then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
703+ else Nothing
704+
368705data EventEnv x
369− = EventEnv { eventEnvSpiderTimeline :: !(SpiderTimeline x)
370− , eventEnvAssignments :: !(IORef [SomeAssignment]) -- Needed for Subscribe
371− , eventEnvHoldInits :: !(IORef [SomeHoldInit]) -- Needed for Subscribe
372− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate])
373− , eventEnvMergeInits :: !(IORef [SomeMergeInit]) -- Needed for Subscribe
706+ = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
707+ , eventEnvHoldInits :: !(IORef [SomeHoldInit x]) -- Needed for Subscribe
708+ , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
709+ , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
374710 , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
375711 , eventEnvRootClears :: !(IORef [SomeRootClear])
376712 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
377− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence]) -- Needed for Subscribe
378− , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
713+ , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
714+ , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
379715 }
380716
381717{-# INLINE runEventM #-}
382−runEventM :: EventM x a -> EventEnv x -> IO a
383−runEventM = runReaderT . unEventM
718+runEventM :: EventM x a -> IO a
719+runEventM = unEventM
720+
721+asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
722+asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
384723
385724class MonadIO m => Defer a m where
386725 getDeferralQueue :: m (IORef [a])
@@ -391,91 +730,91 @@ defer a = do
391730 q <- getDeferralQueue
392731 liftIO $ modifyIORef' q (a:)
393732
394−instance Defer SomeAssignment (EventM x) where
733+instance HasSpiderTimeline x => Defer (SomeAssignment x) (EventM x) where
395734 {-# INLINE getDeferralQueue #-}
396− getDeferralQueue = EventM $ asks eventEnvAssignments
735+ getDeferralQueue = asksEventEnv eventEnvAssignments
397736
398−instance Defer SomeHoldInit (EventM x) where
737+instance HasSpiderTimeline x => Defer (SomeHoldInit x) (EventM x) where
399738 {-# INLINE getDeferralQueue #-}
400− getDeferralQueue = EventM $ asks eventEnvHoldInits
739+ getDeferralQueue = asksEventEnv eventEnvHoldInits
401740
402−instance Defer SomeHoldInit BehaviorM where
741+instance Defer (SomeHoldInit x) (BehaviorM x) where
403742 {-# INLINE getDeferralQueue #-}
404743 getDeferralQueue = BehaviorM $ asks snd
405744
406−instance Defer SomeMergeUpdate (EventM x) where
745+instance HasSpiderTimeline x => Defer (SomeMergeUpdate x) (EventM x) where
407746 {-# INLINE getDeferralQueue #-}
408− getDeferralQueue = EventM $ asks eventEnvMergeUpdates
747+ getDeferralQueue = asksEventEnv eventEnvMergeUpdates
409748
410−instance Defer SomeMergeInit (EventM x) where
749+instance HasSpiderTimeline x => Defer (SomeMergeInit x) (EventM x) where
411750 {-# INLINE getDeferralQueue #-}
412− getDeferralQueue = EventM $ asks eventEnvMergeInits
751+ getDeferralQueue = asksEventEnv eventEnvMergeInits
413752
414−class HasCurrentHeight m where
753+class HasSpiderTimeline x => HasCurrentHeight x m | m -> x where
415754 getCurrentHeight :: m Height
416− scheduleMerge :: Height -> MergeSubscribed a -> m ()
755+ scheduleMerge :: Height -> EventM x () -> m ()
417756
418−instance HasCurrentHeight (EventM x) where
757+instance HasSpiderTimeline x => HasCurrentHeight x (EventM x) where
419758 {-# INLINE getCurrentHeight #-}
420− getCurrentHeight = EventM $ do
421− heightRef <- asks eventEnvCurrentHeight
759+ getCurrentHeight = do
760+ heightRef <- asksEventEnv eventEnvCurrentHeight
422761 liftIO $ readIORef heightRef
423762 {-# INLINE scheduleMerge #-}
424− scheduleMerge height subscribed = EventM $ do
425− delayedRef <- asks eventEnvDelayedMerges
426− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [DelayedMerge subscribed]
427−
428−class HasSpiderTimeline x m | m -> x where
429− -- | Retrieve the current SpiderTimeline
430− askSpiderTimeline :: m (SpiderTimeline x)
763+ scheduleMerge height subscribed = do
764+ delayedRef <- asksEventEnv eventEnvDelayedMerges
765+ liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [subscribed]
431766
432−instance HasSpiderTimeline x (EventM x) where
433− {-# INLINE askSpiderTimeline #-}
434− askSpiderTimeline = EventM $ asks eventEnvSpiderTimeline
767+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
768+class x ~ Global => HasSpiderTimeline x where
769+#else
770+class HasSpiderTimeline x where
771+#endif
772+ -- | Retrieve the current SpiderTimelineEnv
773+ spiderTimeline :: SpiderTimelineEnv x
435774
436−instance HasSpiderTimeline x (SpiderHost x) where
437− {-# INLINE askSpiderTimeline #-}
438− askSpiderTimeline = SpiderHost ask
775+instance HasSpiderTimeline Global where
776+ spiderTimeline = globalSpiderTimelineEnv
439777
440−putCurrentHeight :: Height -> EventM x ()
441−putCurrentHeight h = EventM $ do
442− heightRef <- asks eventEnvCurrentHeight
778+putCurrentHeight :: HasSpiderTimeline x => Height -> EventM x ()
779+putCurrentHeight h = do
780+ heightRef <- asksEventEnv eventEnvCurrentHeight
443781 liftIO $ writeIORef heightRef $! h
444782
445−instance Defer SomeClear (EventM x) where
783+instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
446784 {-# INLINE getDeferralQueue #-}
447− getDeferralQueue = EventM $ asks eventEnvClears
785+ getDeferralQueue = asksEventEnv eventEnvClears
448786
449787{-# INLINE scheduleClear #-}
450788scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
451789scheduleClear r = defer $ SomeClear r
452790
453−instance Defer SomeRootClear (EventM x) where
791+instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
454792 {-# INLINE getDeferralQueue #-}
455− getDeferralQueue = EventM $ asks eventEnvRootClears
793+ getDeferralQueue = asksEventEnv eventEnvRootClears
456794
457795{-# INLINE scheduleRootClear #-}
458796scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
459797scheduleRootClear r = defer $ SomeRootClear r
460798
461−instance Defer SomeResetCoincidence (EventM x) where
799+instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
462800 {-# INLINE getDeferralQueue #-}
463− getDeferralQueue = EventM $ asks eventEnvResetCoincidences
801+ getDeferralQueue = asksEventEnv eventEnvResetCoincidences
464802
465803-- Note: hold cannot examine its event until after the phase is over
466−{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> EventM x (Hold p a) #-}
467−{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> ComputeM (Hold p a) #-}
468−hold :: (R.Patch p, Defer SomeHoldInit m) => a -> Event (p a) -> m (Hold p a)
804+{- SPECIALIZE hold :: (HasSpiderTimeline x, Patch p) => PatchTarget p -> Event x p -> EventM x (Hold x p) #-}
805+{-# INLINE [1] hold #-}
806+hold :: (Patch p, Defer (SomeHoldInit x) m) => PatchTarget p -> Event x p -> m (Hold x p)
469807hold v0 e = do
470808 valRef <- liftIO $ newIORef v0
471809 invsRef <- liftIO $ newIORef []
472− parentRef <- liftIO $ newIORef $ Left e
810+ parentRef <- liftIO $ newIORef Nothing
473811#ifdef DEBUG_NODEIDS
474812 nodeId <- liftIO newNodeId
475813#endif
476814 let h = Hold
477815 { holdValue = valRef
478816 , holdInvalidators = invsRef
817+ , holdEvent = e
479818 , holdParent = parentRef
480819#ifdef DEBUG_NODEIDS
481820 , holdNodeId = nodeId
@@ -485,37 +824,37 @@ hold v0 e = do
485824 return h
486825
487826{-# INLINE getHoldEventSubscription #-}
488−getHoldEventSubscription :: forall m p a x. (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeClear m, R.Patch p, HasCurrentHeight m, Defer SomeMergeInit m, Defer SomeResetCoincidence m, HasSpiderTimeline x m) => Hold p a -> m (EventSubscription (p a))
827+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)
489828getHoldEventSubscription h = do
490829 ep <- liftIO $ readIORef $ holdParent h
491830 case ep of
492− Right subd -> return subd
493− Left e -> do
831+ Just subd -> return subd
832+ Nothing -> do
833+ let e = holdEvent h
494834 subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
495− subscription@(EventSubscription _ subd) <- subscribe e =<< liftIO (newSubscriberHold h)
835+ (subscription@(EventSubscription _ _), occ) <- subscribeAndRead e =<< liftIO (newSubscriberHold h)
496836 liftIO $ writeIORef subscriptionRef $! subscription
497− occ <- liftIO $ readEventSubscribed subd
498837 case occ of
499838 Nothing -> return ()
500839 Just o -> do
501840 old <- liftIO $ readIORef $ holdValue h
502− case R.apply o old of
841+ case apply o old of
503842 Nothing -> return ()
504843 Just new -> do
505844 -- Need to evaluate these so that we don't retain the Hold itself
506845 v <- liftIO $ evaluate $ holdValue h
507846 i <- liftIO $ evaluate $ holdInvalidators h
508847 defer $ SomeAssignment v i new
509− liftIO $ writeIORef (holdParent h) $ Right subscription
848+ liftIO $ writeIORef (holdParent h) $ Just subscription
510849 return subscription
511850
512−type BehaviorEnv = (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]), IORef [SomeHoldInit])
851+type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
513852
514853--type role BehaviorM representational
515854-- BehaviorM can sample behaviors
516−newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT BehaviorEnv IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
855+newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
517856
518−instance Monad BehaviorM where
857+instance Monad (BehaviorM x) where
519858 {-# INLINE (>>=) #-}
520859 BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
521860 {-# INLINE (>>) #-}
@@ -525,94 +864,74 @@ instance Monad BehaviorM where
525864 {-# INLINE fail #-}
526865 fail s = BehaviorM $ fail s
527866
528−data BehaviorSubscribed a
529− = forall p. BehaviorSubscribedHold (Hold p a)
530− | BehaviorSubscribedPull (PullSubscribed a)
867+data BehaviorSubscribed x a
868+ = forall p. BehaviorSubscribedHold (Hold x p)
869+ | BehaviorSubscribedPull (PullSubscribed x a)
531870
532−data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
871+data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
533872
534873--type role PullSubscribed representational
535−data PullSubscribed a
874+data PullSubscribed x a
536875 = PullSubscribed { pullSubscribedValue :: !a
537− , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
538− , pullSubscribedOwnInvalidator :: !Invalidator
539− , pullSubscribedParents :: ![SomeBehaviorSubscribed] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
876+ , pullSubscribedInvalidators :: !(IORef [Weak (Invalidator x)])
877+ , pullSubscribedOwnInvalidator :: !(Invalidator x)
878+ , pullSubscribedParents :: ![SomeBehaviorSubscribed x] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
540879 }
541880
542881--type role Pull representational
543−data Pull a
544− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
545− , pullCompute :: !(BehaviorM a)
882+data Pull x a
883+ = Pull { pullValue :: !(IORef (Maybe (PullSubscribed x a)))
884+ , pullCompute :: !(BehaviorM x a)
546885#ifdef DEBUG_NODEIDS
547886 , pullNodeId :: Int
548887#endif
549888 }
550889
551−data Invalidator
552− = forall a. InvalidatorPull (Pull a)
553− | forall a. InvalidatorSwitch (SwitchSubscribed a)
890+data Invalidator x
891+ = forall a. InvalidatorPull (Pull x a)
892+ | forall a. InvalidatorSwitch (SwitchSubscribed x a)
554893
555−data RootSubscribed a = forall k. GCompare k => RootSubscribed
894+data RootSubscribed x a = forall k. GCompare k => RootSubscribed
556895 { rootSubscribedKey :: !(k a)
557− , rootSubscribedCachedSubscribed :: !(IORef (DMap k RootSubscribed)) -- From the original Root
558− , rootSubscribedSubscribers :: !(SubscriberList a)
896+ , rootSubscribedCachedSubscribed :: !(IORef (DMap k (RootSubscribed x))) -- From the original Root
897+ , rootSubscribedSubscribers :: !(WeakBag (Subscriber x a))
559898 , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
560899 , rootSubscribedUninit :: IO ()
561− , rootSubscribedWeakSelf :: !(IORef (Weak (RootSubscribed a))) --TODO: Can we make this a lazy non-IORef and then force it manually to avoid an indirection each time we use it?
900+ , 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?
562901#ifdef DEBUG_NODEIDS
563902 , rootSubscribedNodeId :: Int
564903#endif
565904 }
566905
567−data Root (k :: * -> *)
906+data Root x (k :: * -> *)
568907 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
569− , rootSubscribed :: !(IORef (DMap k RootSubscribed))
570− , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
908+ , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
909+ , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
571910 }
572911
573−data SomeHoldInit = forall p a. R.Patch p => SomeHoldInit !(Hold p a)
574−
575−data SomeMergeUpdate = forall k. GCompare k => SomeMergeUpdate (R.PatchDMap (DMap k Event)) (MergeSubscribed k)
912+data SomeHoldInit x = forall p. Patch p => SomeHoldInit !(Hold x p)
576913
577−data SomeMergeInit = forall k. GCompare k => SomeMergeInit !(MergeSubscribed k) !(Event (R.PatchDMap (DMap k Event)))
914+data SomeMergeUpdate x = SomeMergeUpdate
915+ { _someMergeUpdate_update :: !(EventM x [EventSubscription x])
916+ , _someMergeUpdate_invalidateHeight :: !(IO ())
917+ , _someMergeUpdate_recalculateHeight :: !(IO ())
918+ }
578919
579−data SomeEventSubscription = forall a. SomeEventSubscription (EventSubscription a)
920+newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
580921
581922-- EventM can do everything BehaviorM can, plus create holds
582−newtype EventM x a = EventM { unEventM :: ReaderT (EventEnv x) 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
583−
584−data PushSubscribed a b
585− = PushSubscribed { pushSubscribedCachedSubscribed :: !(IORef (Maybe (PushSubscribed a b)))
586− , 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)
587− , pushSubscribedHeight :: !(IORef Height)
588− , pushSubscribedSubscribers :: !(SubscriberList b)
589− , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die --TODO: Probably unneeded now
590− , pushSubscribedWeakSelf :: !(IORef (Weak (PushSubscribed a b)))
591− , pushSubscribedParent :: !(EventSubscription a)
592−#ifdef DEBUG_NODEIDS
593− , pushSubscribedNodeId :: Int
594−#endif
595− }
923+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
596924
597−newtype ComputeM a = ComputeM (ReaderT (IORef [SomeHoldInit]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
925+newtype ComputeM x a = ComputeM (ReaderT (IORef [SomeHoldInit x]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
598926
599−instance Defer SomeHoldInit ComputeM where
927+instance Defer (SomeHoldInit x) (ComputeM x) where
600928 getDeferralQueue = ComputeM ask
601929
602−{-# SPECIALIZE runComputeM :: ComputeM a -> EventM x a #-}
603−runComputeM :: Defer SomeHoldInit m => ComputeM a -> m a
930+{-# INLINABLE runComputeM #-}
931+runComputeM :: Defer (SomeHoldInit x) m => ComputeM x a -> m a
604932runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
605933
606−data Push a b
607− = Push { pushCompute :: !(a -> ComputeM (Maybe b)) -- Compute the current firing value; assumes that its parent has been computed --TODO: This only uses Defer SomeHoldInit EventM; perhaps only the hold init queue should be passed in (also, perhaps those hold inits can be accelerated - can there be a loop we would need to worry about?)
608− , pushParent :: !(Event a)
609− , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
610− }
611−
612−data MergeSubscribedParent k a = MergeSubscribedParent !(EventSubscription a) !(Subscriber a)
613−
614−mergeSubscribedParentSubscription :: MergeSubscribedParent k a -> EventSubscription a
615−mergeSubscribedParentSubscription (MergeSubscribedParent es _) = es
934+newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
616935
617936data HeightBag = HeightBag
618937 { _heightBag_size :: {-# UNPACK #-} !Int
@@ -656,252 +975,108 @@ heightBagVerify b@(HeightBag s c) = if
656975heightBagVerify = id
657976#endif
658977
659−data MergeSubscribed k
660− = MergeSubscribed { mergeSubscribedCachedSubscribed :: !(IORef (Maybe (MergeSubscribed k))) -- From the original merge
661− , mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
662− , mergeSubscribedAccum :: !(IORef (DMap k Identity)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
663− , mergeSubscribedHeight :: !(IORef Height)
664− , mergeSubscribedHeightBag :: !(IORef HeightBag)
665− , mergeSubscribedSubscribers :: !(SubscriberList (DMap k Identity))
666− , mergeSubscribedChange :: !(IORef (Subscriber (R.PatchDMap (DMap k Event)), EventSubscribed (R.PatchDMap (DMap k Event))))
667− , mergeSubscribedParents :: !(IORef (DMap k (MergeSubscribedParent k)))
668− , mergeSubscribedWeakSelf :: !(IORef (Weak (MergeSubscribed k)))
669−#ifdef DEBUG_NODEIDS
670− , mergeSubscribedNodeId :: Int
671−#endif
672− }
673−
674−--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
675−data Merge k
676− = Merge { mergeParents :: !(Dynamic R.PatchDMap (DMap k Event))
677− , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k)))
678− }
679−
680−data FanSubscribedChildren k a = FanSubscribedChildren
681− { _fanSubscribedChildren_list :: !(SubscriberList a)
682− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed k)
683− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed k)))
978+data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
979+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
980+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
981+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
684982 }
685983
686−data FanSubscribed k
687− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed k)))
688− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren k))) -- This DMap should never be empty
689− , fanSubscribedParent :: !(EventSubscription (DMap k Identity))
984+data FanSubscribed (x :: *) k
985+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
986+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
987+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
988+ , fanSubscribedParent :: !(EventSubscription x)
690989#ifdef DEBUG_NODEIDS
691990 , fanSubscribedNodeId :: Int
692991#endif
693992 }
694993
695−data Fan k
696− = Fan { fanParent :: !(Event (DMap k Identity))
697− , fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
994+data Fan x k
995+ = Fan { fanParent :: !(Event x (DMap k Identity))
996+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
698997 }
699998
700−data SwitchSubscribed a
701− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
999+data SwitchSubscribed x a
1000+ = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
7021001 , switchSubscribedOccurrence :: !(IORef (Maybe a))
7031002 , switchSubscribedHeight :: !(IORef Height)
704− , switchSubscribedSubscribers :: !(SubscriberList a)
705− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !Invalidator
706− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
707− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
708− , switchSubscribedParent :: !(Behavior (Event a))
709− , switchSubscribedCurrentParent :: !(IORef (EventSubscription a))
710− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed a)))
1003+ , switchSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1004+ , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !(Invalidator x)
1005+ , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak (Invalidator x)))
1006+ , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed x])
1007+ , switchSubscribedParent :: !(Behavior x (Event x a))
1008+ , switchSubscribedCurrentParent :: !(IORef (EventSubscription x))
1009+ , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed x a)))
7111010#ifdef DEBUG_NODEIDS
7121011 , switchSubscribedNodeId :: Int
7131012#endif
7141013 }
7151014
716−data Switch a
717− = Switch { switchParent :: !(Behavior (Event a))
718− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
1015+data Switch x a
1016+ = Switch { switchParent :: !(Behavior x (Event x a))
1017+ , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
7191018 }
7201019
7211020{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
722−data CoincidenceSubscribed a
723− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
1021+data CoincidenceSubscribed x a
1022+ = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
7241023 , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
725− , coincidenceSubscribedSubscribers :: !(SubscriberList a)
1024+ , coincidenceSubscribedSubscribers :: !(WeakBag (Subscriber x a))
7261025 , coincidenceSubscribedHeight :: !(IORef Height)
727− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
728− , coincidenceSubscribedOuterParent :: !(EventSubscription (Event a))
729− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
730− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed a)))
1026+ , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber x (Event x a))
1027+ , coincidenceSubscribedOuterParent :: !(EventSubscription x)
1028+ , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed x)))
1029+ , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed x a)))
7311030#ifdef DEBUG_NODEIDS
7321031 , coincidenceSubscribedNodeId :: Int
7331032#endif
7341033 }
7351034
736−data Coincidence a
737− = Coincidence { coincidenceParent :: !(Event (Event a))
738− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
1035+data Coincidence x a
1036+ = Coincidence { coincidenceParent :: !(Event x (Event x a))
1037+ , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
7391038 }
7401039
741−data Subscriber a
742− = forall b. SubscriberPush (a -> ComputeM (Maybe b)) (PushSubscribed a b) --TODO: Why does this have the pushCompute in it?
743− | forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k) --TODO: Can we inline the GCompare?
744− | forall k. (a ~ R.PatchDMap (DMap k Event), GCompare k) => SubscriberMergeChange (MergeSubscribed k)
745− | forall k. (GCompare k, a ~ DMap k Identity) => SubscriberFan (FanSubscribed k)
746− | forall p b. (a ~ p b, R.Patch p) => SubscriberHold !(Hold p b)
747− | forall b. (a ~ Identity b) => SubscriberHoldIdentity !(Hold Identity b)
748− | SubscriberSwitch (SwitchSubscribed a)
749− | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
750− | SubscriberCoincidenceInner (CoincidenceSubscribed a)
751− | SubscriberHandle
752−
753−-- These function are constructor functions that are marked NOINLINE so they are
754−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
755−-- so any weak references that are attached to the constructors will have their
756−-- finalizer run. Using the opaque constructor, does not see the
757−-- constructor application, so it behaves like an IORef and cannot be fused away.
758−--
759−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
760−-- the weak pointer to it in some cases.
761−
762−--TODO: I think all of these noinlines are now unnecessary
763−{-# NOINLINE newSubscriberPush #-}
764−newSubscriberPush :: (a -> ComputeM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
765−newSubscriberPush compute subd = return $! SubscriberPush compute subd
766−
767−{-# RULES
768− "newSubscriberHold/Identity" forall h. newSubscriberHold h = newSubscriberHoldIdentity h
769− #-}
770−{-# NOINLINE newSubscriberHold #-}
771−newSubscriberHold :: R.Patch p => Hold p a -> IO (Subscriber (p a))
772−newSubscriberHold h = return $! SubscriberHold h
773−
774−{-# NOINLINE newSubscriberHoldIdentity #-}
775−newSubscriberHoldIdentity :: Hold Identity a -> IO (Subscriber (Identity a))
776−newSubscriberHoldIdentity h = return $! SubscriberHoldIdentity h
777−
778−{-# NOINLINE newSubscriberMerge #-}
779−newSubscriberMerge :: GCompare k => k a -> MergeSubscribed k -> IO (Subscriber a)
780−newSubscriberMerge k subd = return $! SubscriberMerge k subd
781−
782−{-# NOINLINE newSubscriberMergeChange #-}
783−newSubscriberMergeChange :: GCompare k => MergeSubscribed k -> IO (Subscriber (R.PatchDMap (DMap k Event)))
784−newSubscriberMergeChange subd = return $! SubscriberMergeChange subd
785−
786−{-# NOINLINE newSubscriberFan #-}
787−newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
788−newSubscriberFan subd = return $! SubscriberFan subd
789−
790−{-# NOINLINE newSubscriberSwitch #-}
791−newSubscriberSwitch :: SwitchSubscribed a -> IO (Subscriber a)
792−newSubscriberSwitch subd = return $! SubscriberSwitch subd
793−
794−{-# NOINLINE newSubscriberCoincidenceOuter #-}
795−newSubscriberCoincidenceOuter :: CoincidenceSubscribed b -> IO (Subscriber (Event b))
796−newSubscriberCoincidenceOuter subd = return $! SubscriberCoincidenceOuter subd
797−
798−{-# NOINLINE newSubscriberCoincidenceInner #-}
799−newSubscriberCoincidenceInner :: CoincidenceSubscribed a -> IO (Subscriber a)
800−newSubscriberCoincidenceInner subd = return $! SubscriberCoincidenceInner subd
801−
8021040{-# NOINLINE newInvalidatorSwitch #-}
803−newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
1041+newInvalidatorSwitch :: SwitchSubscribed x a -> IO (Invalidator x)
8041042newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
8051043
8061044{-# NOINLINE newInvalidatorPull #-}
807−newInvalidatorPull :: Pull a -> IO Invalidator
1045+newInvalidatorPull :: Pull x a -> IO (Invalidator x)
8081046newInvalidatorPull p = return $! InvalidatorPull p
8091047
810−data Dynamic p a
811− = DynamicHold !(Hold p a)
812− | p ~ Identity => DynamicHoldIdentity !(Hold Identity a)
813− | DynamicConst !a
814− | DynamicDyn !(Dyn p a)
815− | p ~ Identity => DynamicDynIdentity !(Dyn Identity a)
816−
817−{-# INLINE current #-}
818−current :: R.Patch p => Dynamic p a -> Behavior a
819−current = \case
820− DynamicHold h -> BehaviorHold h
821− DynamicHoldIdentity h -> BehaviorHoldIdentity h
822− DynamicConst a -> BehaviorConst a
823− DynamicDyn d -> BehaviorDyn d
824− DynamicDynIdentity d -> BehaviorDynIdentity d
825−
826−--TODO: If you only need updated, not current, can we avoid actually constructing the Hold?
827−{-# INLINE updated #-}
828−updated :: R.Patch p => Dynamic p a -> Event (p a)
829−updated = \case
830− DynamicHold h -> EventHold h
831− DynamicHoldIdentity h -> EventHoldIdentity h
832− DynamicConst _ -> EventNever
833− DynamicDyn d -> EventDyn d
834− DynamicDynIdentity d -> EventDynIdentity d
835−
836−newMapDyn :: (a -> b) -> Dynamic Identity a -> Dynamic Identity b
837−newMapDyn f d = DynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ current d) (Identity . f . runIdentity <$> updated d)
838−
839−instance Functor (Dynamic Identity) where
840− fmap = newMapDyn
841−
842−instance Applicative (Dynamic Identity) where
843− pure = DynamicConst
844− (<*>) = zipDynWith ($)
845− (*>) = zipDynWith (flip const)
846− (<*) = zipDynWith const
847−
848−instance R.FunctorMaybe Event where
1048+instance HasSpiderTimeline x => FunctorMaybe (Event x) where
8491049 fmapMaybe f = push $ return . f
8501050
851−instance Align Event where
852− nil = EventNever
853− align ea eb = R.fmapMaybe R.dmapToThese $ merge $ DynamicConst $ DMap.fromList [R.LeftTag :=> ea, R.RightTag :=> eb]
1051+instance HasSpiderTimeline x => Align (Event x) where
1052+ nil = eventNever
1053+ align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1054+
1055+newtype Dyn x p = Dyn { unDyn :: IORef (Either (BehaviorM x (PatchTarget p), Event x p) (Hold x p)) }
1056+
1057+newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1058+newMapDyn f d = dynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
8541059
8551060--TODO: Avoid the duplication between this and R.zipDynWith
856−zipDynWith :: (a -> b -> c) -> Dynamic Identity a -> Dynamic Identity b -> Dynamic Identity c
1061+zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
8571062zipDynWith f da db =
858− let eab = align (updated da) (updated db)
1063+ let eab = align (dynamicUpdated da) (dynamicUpdated db)
8591064 ec = flip push eab $ \o -> do
8601065 (a, b) <- case o of
8611066 This (Identity a) -> do
862− b <- readBehaviorUntracked $ current db
1067+ b <- readBehaviorUntracked $ dynamicCurrent db
8631068 return (a, b)
8641069 That (Identity b) -> do
865− a <- readBehaviorUntracked $ current da
1070+ a <- readBehaviorUntracked $ dynamicCurrent da
8661071 return (a, b)
8671072 These (Identity a) (Identity b) -> return (a, b)
8681073 return $ Just $ Identity $ f a b
869− in DynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (current da) <*> readBehaviorUntracked (current db)) ec
1074+ in dynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (dynamicCurrent da) <*> readBehaviorUntracked (dynamicCurrent db)) ec
8701075
871−instance Monad (Dynamic Identity) where
872− {-# INLINE return #-}
873− return = pure
874− {-# INLINE (>>=) #-}
875− x >>= f = DynamicDynIdentity $ newJoinDyn $ newMapDyn f x --TODO: (>>), fail
876− {-# INLINE (>>) #-}
877− _ >> y = y
878− {-# INLINE fail #-}
879− fail _ = error "Dynamic does not support 'fail'"
880−
881−newtype Dyn p a = Dyn { unDyn :: IORef (Either (BehaviorM a, Event (p a)) (Hold p a)) }
882−
883−unsafeDyn :: BehaviorM a -> Event (p a) -> Dyn p a
1076+unsafeDyn :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
8841077unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
8851078 where x = (readV0, v')
8861079
887−newJoinDyn :: Dynamic Identity (Dynamic Identity a) -> Dyn Identity a
888−newJoinDyn d =
889− let readV0 = readBehaviorTracked . current =<< readBehaviorTracked (current d)
890− eOuter = push (fmap (Just . Identity) . readBehaviorUntracked . current . runIdentity) $ updated d
891− eInner = switch $ updated <$> current d
892− eBoth = coincidence $ updated . runIdentity <$> updated d
893− v' = unSpiderEvent $ R.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
894− in unsafeDyn readV0 v'
895−
896−--type role Behavior representational
897−data Behavior a
898− = forall p. BehaviorHold !(Hold p a)
899− | BehaviorHoldIdentity !(Hold Identity a)
900− | BehaviorConst !a
901− | BehaviorPull !(Pull a)
902− | BehaviorDynIdentity !(Dyn Identity a)
903− | forall p. R.Patch p => BehaviorDyn !(Dyn p a)
904−
9051080-- ResultM can read behaviors and events
9061081type ResultM = EventM
9071082
@@ -909,24 +1084,19 @@ type ResultM = EventM
9091084unsafeNewIORef :: a -> b -> IORef b
9101085unsafeNewIORef _ b = unsafePerformIO $ newIORef b
9111086
912−instance Functor Event where
1087+instance HasSpiderTimeline x => Functor (Event x) where
9131088 fmap f = push $ return . Just . f
9141089
915−instance Functor Behavior where
1090+instance Functor (Behavior x) where
9161091 fmap f = pull . fmap f . readBehaviorTracked
9171092
918−{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
919−push :: forall a b. (a -> ComputeM (Maybe b)) -> Event a -> Event b
920−push f e = EventPush $ Push
921− { pushCompute = f
922− , pushParent = e
923− , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
924− }
925−-- DISABLED: {- RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
1093+{-# INLINE push #-}
1094+push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
1095+push f e = cacheEvent (pushCheap f e)
9261096
927−{-# NOINLINE pull #-}
928−pull :: BehaviorM a -> Behavior a
929−pull a = BehaviorPull $ Pull
1097+{-# INLINABLE pull #-}
1098+pull :: BehaviorM x a -> Behavior x a
1099+pull a = behaviorPull $ Pull
9301100 { pullCompute = a
9311101 , pullValue = unsafeNewIORef a Nothing
9321102#ifdef DEBUG_NODEIDS
@@ -934,29 +1104,25 @@ pull a = BehaviorPull $ Pull
9341104#endif
9351105 }
9361106
937−{-# NOINLINE switch #-}
938−switch :: Behavior (Event a) -> Event a
939−switch a = EventSwitch $ Switch
1107+{-# INLINABLE switch #-}
1108+switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1109+switch a = eventSwitch $ Switch
9401110 { switchParent = a
9411111 , switchSubscribed = unsafeNewIORef a Nothing
9421112 }
9431113
944−coincidence :: Event (Event a) -> Event a
945−coincidence a = EventCoincidence $ Coincidence
1114+coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1115+coincidence a = eventCoincidence $ Coincidence
9461116 { coincidenceParent = a
9471117 , coincidenceSubscribed = unsafeNewIORef a Nothing
9481118 }
9491119
950−propagateAndUpdateSubscribersRef :: SubscriberList a -> a -> EventM x ()
951−propagateAndUpdateSubscribersRef subscribersRef a = do
952− propagate a subscribersRef
953−
9541120-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
955−run :: [DSum RootTrigger Identity] -> ResultM x b -> SpiderHost x b
1121+run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
9561122run roots after = do
957− tracePropagate $ "Running an event frame with " <> show (length roots) <> " events"
958− spiderEnv <- SpiderHost ask
959− result <- SpiderHost $ lift $ withMVar (_spiderEnv_lock spiderEnv) $ \_ -> flip runReaderT spiderEnv $ unSpiderHost $ runFrame $ do
1123+ tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1124+ t <- SpiderHost ask
1125+ result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock t) $ \_ -> flip runReaderT t $ unSpiderHost $ runFrame $ do
9601126 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
9611127 occBefore <- liftIO $ do
9621128 occBefore <- readIORef occRef
@@ -967,43 +1133,38 @@ run roots after = do
9671133 return $ Just r
9681134 else return Nothing
9691135 forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
970− propagateAndUpdateSubscribersRef subscribersRef a
971− delayedRef <- EventM $ asks eventEnvDelayedMerges
1136+ propagate a subscribersRef
1137+ delayedRef <- asksEventEnv eventEnvDelayedMerges
9721138 let go = do
9731139 delayed <- liftIO $ readIORef delayedRef
9741140 case IntMap.minViewWithKey delayed of
9751141 Nothing -> return ()
9761142 Just ((currentHeight, cur), future) -> do
977− tracePropagate $ "Running height " ++ show currentHeight
1143+ tracePropagate (Proxy :: Proxy x) $ "Running height " ++ show currentHeight
9781144 putCurrentHeight $ Height currentHeight
9791145 liftIO $ writeIORef delayedRef $! future
980− forM_ cur $ \d -> case d of
981− DelayedMerge subscribed -> do
982− height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
983− case height `compare` Height currentHeight of
984− LT -> error "Somehow a merge's height has been decreased after it was scheduled"
985− GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
986− EQ -> do
987− m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
988− liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! DMap.empty
989− --TODO: Assert that m is not empty
990− liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
991− scheduleClear $ mergeSubscribedOccurrence subscribed
992− propagateAndUpdateSubscribersRef (mergeSubscribedSubscribers subscribed) m
1146+ sequence_ cur
9931147 go
9941148 go
9951149 putCurrentHeight maxBound
9961150 after
997− tracePropagate "Done running an event frame"
1151+ tracePropagate (Proxy :: Proxy x) "Done running an event frame"
9981152 return result
9991153
1154+scheduleMerge' :: HasSpiderTimeline x => Height -> IORef Height -> EventM x () -> EventM x ()
1155+scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
1156+ height <- liftIO $ readIORef heightRef
1157+ currentHeight <- getCurrentHeight
1158+ case height `compare` currentHeight of
1159+ LT -> error "Somehow a merge's height has been decreased after it was scheduled"
1160+ GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1161+ EQ -> a
1162+
10001163data SomeClear = forall a. SomeClear (IORef (Maybe a))
10011164
10021165data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
10031166
1004−data SomeAssignment = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak Invalidator]) a
1005−
1006−data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
1167+data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
10071168
10081169debugFinalize :: Bool
10091170debugFinalize = False
@@ -1022,41 +1183,37 @@ type WeakList a = [Weak a]
10221183#ifdef DEBUG
10231184withIncreasedDepth :: CanTrace x m => m a -> m a
10241185withIncreasedDepth a = do
1025− spiderEnv <- askSpiderTimeline
1026− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) succ
1186+ spiderTimeline <- askSpiderTimelineEnv
1187+ liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
10271188 result <- a
1028− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) pred
1189+ liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
10291190 return result
10301191#else
10311192withIncreasedDepth :: m a -> m a
10321193withIncreasedDepth = id
10331194#endif
10341195
1035−type CanTrace x m = (HasSpiderTimeline x m, MonadIO m)
1196+type CanTrace x m = (HasSpiderTimeline x, MonadIO m)
10361197
10371198{-# INLINE tracePropagate #-}
1038−tracePropagate :: (CanTrace x m) => String -> m ()
1039−tracePropagate = traceWhen debugPropagate
1040−
1041−{-# INLINE traceSubscribe #-}
1042−traceSubscribe :: String -> IO ()
1043−traceSubscribe = when debugSubscribe . liftIO . putStrLn
1199+tracePropagate :: (CanTrace x m) => proxy x -> String -> m ()
1200+tracePropagate p = traceWhen p debugPropagate
10441201
10451202{-# INLINE traceInvalidate #-}
10461203traceInvalidate :: String -> IO ()
10471204traceInvalidate = when debugInvalidate . liftIO . putStrLn
10481205
10491206{-# INLINE traceWhen #-}
1050−traceWhen :: (CanTrace x m) => Bool -> String -> m ()
1051−traceWhen b message = traceMWhen b $ return message
1207+traceWhen :: (CanTrace x m) => proxy x -> Bool -> String -> m ()
1208+traceWhen p b message = traceMWhen p b $ return message
10521209
10531210{-# INLINE traceMWhen #-}
1054−traceMWhen :: (CanTrace x m) => Bool -> m String -> m ()
1055−traceMWhen b getMessage = when b $ do
1211+traceMWhen :: (CanTrace x m) => proxy x -> Bool -> m String -> m ()
1212+traceMWhen _ b getMessage = when b $ do
10561213 message <- getMessage
10571214#ifdef DEBUG
1058− spiderEnv <- askSpiderTimeline
1059− d <- liftIO $ readIORef $ _spiderEnv_depth spiderEnv
1215+ spiderTimeline <- askSpiderTimelineEnv
1216+ d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
10601217#else
10611218 let d = 0
10621219#endif
@@ -1065,6 +1222,7 @@ traceMWhen b getMessage = when b $ do
10651222whoCreatedIORef :: IORef a -> IO [String]
10661223whoCreatedIORef (IORef a) = whoCreated $! a
10671224
1225+#ifdef DEBUG_CYCLES
10681226groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
10691227groupByHead = \case
10701228 [] -> []
@@ -1076,181 +1234,49 @@ groupByHead = \case
10761234
10771235listsToForest :: Eq a => [[a]] -> Forest a
10781236listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1079−
1080−-- | Propagate everything at the current height
1081−propagate :: a -> SubscriberList a -> EventM x ()
1082−propagate a subscribers = withIncreasedDepth $ do
1083− -- 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
1084− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
1085− WeakBag.traverse subscribers $ \s -> case s of
1086− SubscriberPush compute subscribed -> {-# SCC "traversePush" #-} do
1087− tracePropagate $ "SubscriberPush" <> showNodeId subscribed
1088− occ <- runComputeM $ compute a
1089− case occ of
1090− Nothing -> return () -- No need to write a Nothing back into the Ref
1091− Just o -> do
1092− liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
1093− scheduleClear $ pushSubscribedOccurrence subscribed
1094− propagate o $ pushSubscribedSubscribers subscribed
1095− SubscriberMerge k subscribed -> {-# SCC "traverseMerge" #-} do
1096− tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
1097− oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
1098− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge" <> showNodeId subscribed) k (Identity a) oldM
1099− tracePropagate $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1100− liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! newM
1101− when (DMap.null oldM) $ do -- Only schedule the firing once
1102− height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
1103− --TODO: assertions about height
1104− currentHeight <- getCurrentHeight
1105− when (height <= currentHeight) $ do
1106− myStack <- liftIO $ whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
1107− if height /= invalidHeight
1108− then error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1109− else liftIO $ do
1110− nodesInvolvedInCycle <- walkInvalidHeightParents $ EventSubscribedMerge subscribed
1111− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1112− error $ "Causality loop found:\n" <> drawForest (listsToForest stacks)
1113− scheduleMerge height subscribed
1114− SubscriberMergeChange subscribed -> {-# SCC "traverseMergeChange" #-} do
1115− tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
1116− defer $ SomeMergeUpdate a subscribed
1117− SubscriberFan subscribed -> {-# SCC "traverseFan" #-} do
1118− subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
1119− tracePropagate $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
1120− --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
1121− forM_ (DMap.toList a) $ \(k :=> Identity v) -> {-# SCC "SubscriberFan" #-} case DMap.lookup k subs of
1122− Nothing -> do
1123− tracePropagate "No subscriber for key"
1124− return ()
1125− Just subsubs -> do
1126− propagate v $ _fanSubscribedChildren_list subsubs
1127− return ()
1128− SubscriberHold h -> {-# SCC "traverseHold" #-} propagateSubscriberHold h a
1129− SubscriberHoldIdentity h -> {-# SCC "traverseHoldIdentity" #-} propagateSubscriberHold h a
1130− SubscriberSwitch subscribed -> {-# SCC "traverseSwitch" #-} do
1131− tracePropagate $ "SubscriberSwitch" <> showNodeId subscribed
1132− liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
1133− scheduleClear $ switchSubscribedOccurrence subscribed
1134− propagate a $ switchSubscribedSubscribers subscribed
1135− SubscriberCoincidenceOuter subscribed -> {-# SCC "traverseCoincidenceOuter" #-} do
1136− tracePropagate $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
1137− outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
1138− tracePropagate $ " outerHeight = " <> show outerHeight
1139− (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
1140− tracePropagate $ " isJust occ = " <> show (isJust occ)
1141− tracePropagate $ " innerHeight = " <> show innerHeight
1142− liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
1143− scheduleClear $ coincidenceSubscribedInnerParent subscribed
1144− case occ of
1145− Nothing -> do
1146− when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
1147− writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
1148− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
1149− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
1150− Just o -> do -- Since it's already firing, no need to adjust height
1151− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
1152− scheduleClear $ coincidenceSubscribedOccurrence subscribed
1153− propagate o $ coincidenceSubscribedSubscribers subscribed
1154− SubscriberCoincidenceInner subscribed -> {-# SCC "traverseCoincidenceInner" #-} do
1155− tracePropagate $ "SubscriberCoincidenceInner" <> showNodeId subscribed
1156− occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
1157− case occ of
1158− Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
1159− Nothing -> do
1160− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
1161− scheduleClear $ coincidenceSubscribedOccurrence subscribed
1162− propagate a $ coincidenceSubscribedSubscribers subscribed
1163− SubscriberHandle -> {-# SCC "traverseHandle" #-} tracePropagate "SubscriberHandle"
1237+#endif
11641238
11651239{-# INLINE propagateSubscriberHold #-}
1166−propagateSubscriberHold :: R.Patch p => Hold p a -> p a -> EventM x ()
1240+propagateSubscriberHold :: forall x p. (HasSpiderTimeline x, Patch p) => Hold x p -> p -> EventM x ()
11671241propagateSubscriberHold h a = do
1168− {-# SCC "trace" #-} traceMWhen debugPropagate $ liftIO $ do
1242+ {-# SCC "trace" #-} traceMWhen (Proxy :: Proxy x) debugPropagate $ liftIO $ do
11691243 invalidators <- liftIO $ readIORef $ holdInvalidators h
11701244 return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
11711245 v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1172− case {-# SCC "apply" #-} R.apply a v of
1246+ case {-# SCC "apply" #-} apply a v of
11731247 Nothing -> return ()
11741248 Just v' -> do
1175− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1249+ {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate (Proxy :: Proxy x) $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
11761250 vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
11771251 iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
11781252 defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
11791253
1180−data SomeResetCoincidence = forall a. SomeResetCoincidence !(EventSubscription a) !(Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1181−
1182−{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> BehaviorM a #-}
1183−{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> EventM x a #-}
1184−readBehaviorUntracked :: Defer SomeHoldInit m => Behavior a -> m a
1185−readBehaviorUntracked b = do
1186− holdInits <- getDeferralQueue
1187− liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
1254+data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
11881255
1189−runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IORef [SomeHoldInit] -> IO a
1256+runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
11901257runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
11911258
1192−askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
1259+askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
11931260askInvalidator = do
11941261 (!m, _) <- BehaviorM ask
11951262 case m of
11961263 Nothing -> return Nothing
11971264 Just (!wi, _) -> return $ Just wi
11981265
1199−askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
1266+askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
12001267askParentsRef = do
12011268 (!m, _) <- BehaviorM ask
12021269 case m of
12031270 Nothing -> return Nothing
12041271 Just (_, !p) -> return $ Just p
12051272
1206−askBehaviorHoldInits :: BehaviorM (IORef [SomeHoldInit])
1273+askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
12071274askBehaviorHoldInits = do
12081275 (_, !his) <- BehaviorM ask
12091276 return his
12101277
1211−readBehaviorTracked :: Behavior a -> BehaviorM a
1212−readBehaviorTracked b = case b of
1213− BehaviorHold h -> readHoldTracked h
1214− BehaviorHoldIdentity h -> readHoldTracked h
1215− BehaviorConst a -> return a
1216− BehaviorPull p -> do
1217− val <- liftIO $ readIORef $ pullValue p
1218− case val of
1219− Just subscribed -> do
1220− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
1221− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
1222− liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
1223− return $ pullSubscribedValue subscribed
1224− Nothing -> do
1225− i <- liftIO $ newInvalidatorPull p
1226− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
1227− parentsRef <- liftIO $ newIORef []
1228− holdInits <- askBehaviorHoldInits
1229− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
1230− invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
1231− parents <- liftIO $ readIORef parentsRef
1232− let subscribed = PullSubscribed
1233− { pullSubscribedValue = a
1234− , pullSubscribedInvalidators = invsRef
1235− , pullSubscribedOwnInvalidator = i
1236− , pullSubscribedParents = parents
1237− }
1238− liftIO $ writeIORef (pullValue p) $ Just subscribed
1239− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
1240− return a
1241− BehaviorDyn d -> readHoldTracked =<< getDynHold d
1242− BehaviorDynIdentity d -> readHoldTracked =<< getDynHold d
1243−
1244−{-# INLINE readHoldTracked #-}
1245−readHoldTracked :: Hold p a -> BehaviorM a
1246−readHoldTracked h = do
1247− result <- liftIO $ readIORef $ holdValue h
1248− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
1249− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
1250− return result
1251−
12521278{-# INLINE getDynHold #-}
1253−getDynHold :: (Defer SomeHoldInit m, R.Patch p) => Dyn p a -> m (Hold p a)
1279+getDynHold :: (Defer (SomeHoldInit x) m, Patch p) => Dyn x p -> m (Hold x p)
12541280getDynHold d = do
12551281 mh <- liftIO $ readIORef $ unDyn d
12561282 case mh of
@@ -1262,72 +1288,30 @@ getDynHold d = do
12621288 liftIO $ writeIORef (unDyn d) $ Right h
12631289 return h
12641290
1265−readRootSubscribed :: RootSubscribed a -> IO (Maybe a)
1266−readRootSubscribed = rootSubscribedOccurrence
1267−
1268−readPushSubscribed :: PushSubscribed b a -> IO (Maybe a)
1269−readPushSubscribed subscribed = do
1270− result <- readIORef $ pushSubscribedOccurrence subscribed -- Since ResultM is always called after the final height is reached, this will always be valid
1271− touch $ pushSubscribedSelf subscribed
1272− return result
1273−
1274−readMergeSubscribed :: MergeSubscribed k -> IO (Maybe (DMap k Identity))
1275−readMergeSubscribed subscribed = do
1276− result <- readIORef $ mergeSubscribedOccurrence subscribed
1277− touch $ mergeSubscribedParents subscribed
1278− return result
1279−
1280−readFanSubscribed :: GCompare k => k a -> FanSubscribed k -> IO (Maybe a)
1281−readFanSubscribed k fs = do
1282− parentOcc <- readEventSubscribed $ _eventSubscription_subscribed $ fanSubscribedParent fs
1283− return $ coerce $ DMap.lookup k =<< parentOcc
1284−
1285−readSwitchSubscribed :: SwitchSubscribed a -> IO (Maybe a)
1286−readSwitchSubscribed subscribed = do
1287− result <- readIORef $ switchSubscribedOccurrence subscribed
1288− touch $ switchSubscribedOwnInvalidator subscribed
1289− return result
1290−
1291−readCoincidenceSubscribed :: CoincidenceSubscribed a -> IO (Maybe a)
1292−readCoincidenceSubscribed subscribed = do
1293− result <- readIORef $ coincidenceSubscribedOccurrence subscribed
1294− touch $ coincidenceSubscribedOuter subscribed
1295− --TODO: do we need to touch the inner subscriber?
1296− return result
1297−
12981291-- Always refers to 0
12991292{-# NOINLINE zeroRef #-}
13001293zeroRef :: IORef Height
13011294zeroRef = unsafePerformIO $ newIORef zeroHeight
13021295
1303−{-# INLINE subscribeHoldEvent #-}
1304−subscribeHoldEvent :: (CanSubscribe x m, R.Patch p) => Hold p a -> Subscriber (p a) -> m (EventSubscription (p a))
1305−subscribeHoldEvent h sub = do
1306− EventSubscription _ subd <- getHoldEventSubscription h
1307− sln <- liftIO $ subscribeEventSubscribed subd sub
1308− return $ EventSubscription sln subd
1309−
1310−{-# INLINE debugSubscribe #-}
1311−debugSubscribe :: Bool
1312−debugSubscribe = False
1313−
1314−
13151296type CanSubscribe x m =
1316− ( HasSpiderTimeline x m
1317− , HasCurrentHeight m
1318− , Defer SomeHoldInit m, Defer SomeMergeInit m -- The things we need to lazily initialize state
1297+ ( HasSpiderTimeline x
1298+ , HasCurrentHeight x m
1299+ , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
13191300 , Defer SomeClear m -- Wiping out event firings
1320− , Defer SomeAssignment m -- Updating state
1321− , Defer SomeResetCoincidence m
1301+ , Defer (SomeAssignment x) m -- Updating state
1302+ , Defer (SomeResetCoincidence x) m
1303+ , m ~ EventM x
13221304 )
13231305
1324−getRootSubscribed :: GCompare k => k a -> Root k -> Subscriber a -> IO (SubscriberListTicket a, RootSubscribed a)
1306+getRootSubscribed :: GCompare k => k a -> Root x k -> Subscriber x a -> IO (WeakBagTicket, RootSubscribed x a, Maybe a)
13251307getRootSubscribed k r sub = do
13261308 mSubscribed <- readIORef $ rootSubscribed r
1309+ let getOcc = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
13271310 case DMap.lookup k mSubscribed of
13281311 Just subscribed -> {-# SCC "hitRoot" #-} do
13291312 sln <- subscribeRootSubscribed subscribed sub
1330− return (sln, subscribed)
1313+ occ <- getOcc
1314+ return (sln, subscribed, occ)
13311315 Nothing -> {-# SCC "missRoot" #-} do
13321316 weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
13331317 let !cached = rootSubscribed r
@@ -1339,7 +1323,7 @@ getRootSubscribed k r sub = do
13391323 let subscribed = RootSubscribed
13401324 { rootSubscribedKey = k
13411325 , rootSubscribedCachedSubscribed = cached
1342− , rootSubscribedOccurrence = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1326+ , rootSubscribedOccurrence = getOcc
13431327 , rootSubscribedSubscribers = subs
13441328 , rootSubscribedUninit = uninit
13451329 , rootSubscribedWeakSelf = weakSelf
@@ -1349,153 +1333,41 @@ getRootSubscribed k r sub = do
13491333 }
13501334 writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "RootSubscribed"
13511335 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
1352− return (sln, subscribed)
1336+ occ <- getOcc
1337+ return (sln, subscribed, occ)
13531338
13541339{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1355−cleanupRootSubscribed :: RootSubscribed a -> IO ()
1340+cleanupRootSubscribed :: RootSubscribed x a -> IO ()
13561341cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
13571342 rootSubscribedUninit self
13581343 modifyIORef' cached $ DMap.delete k
13591344
13601345{-# INLINE subscribeRootSubscribed #-}
1361−subscribeRootSubscribed :: RootSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1346+subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
13621347subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
13631348
1364−-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
1365−{-# SPECIALIZE getPushSubscribed :: Push a b -> Subscriber b -> EventM x (SubscriberListTicket b, PushSubscribed a b) #-}
1366−getPushSubscribed :: CanSubscribe x m => Push a b -> Subscriber b -> m (SubscriberListTicket b, PushSubscribed a b)
1367−getPushSubscribed p sub = do
1368− mSubscribed <- liftIO $ readIORef $ pushSubscribed p
1369− case mSubscribed of
1370− Just subscribed -> {-# SCC "hitPush" #-} do
1371− sln <- liftIO $ subscribePushSubscribed subscribed sub
1372− return (sln, subscribed)
1373− Nothing -> {-# SCC "missPush" #-} do -- Not yet subscribed
1374− subscribedRef <- liftIO $ newIORef $ error "getPushSubscribed: subscribed not yet intialized"
1375− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1376− s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
1377− subscription@(EventSubscription _ subd) <- subscribe (pushParent p) s
1378− parentOcc <- liftIO $ readEventSubscribed subd
1379− occ <- runComputeM $ join <$> mapM (pushCompute p) parentOcc
1380− occRef <- liftIO $ newIORef occ
1381− when (isJust occ) $ scheduleClear occRef
1382− weakSelf <- liftIO $ newIORef $ error "getPushSubscribed: weakSelf not initialized"
1383− let !cached = pushSubscribed p
1384− (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupPushSubscribed
1385− let subscribed = PushSubscribed
1386− { pushSubscribedCachedSubscribed = cached
1387− , pushSubscribedOccurrence = occRef
1388− , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
1389− , pushSubscribedSubscribers = subs
1390− , pushSubscribedSelf = s
1391− , pushSubscribedWeakSelf = weakSelf
1392− , pushSubscribedParent = subscription
1393−#ifdef DEBUG_NODEIDS
1394− , pushSubscribedNodeId = unsafeNodeId p
1395−#endif
1396− }
1397− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
1398− liftIO $ writeIORef subscribedRef $! subscribed
1399− liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
1400− return (sln, subscribed)
1401−
1402−cleanupPushSubscribed :: PushSubscribed a b -> IO ()
1403−cleanupPushSubscribed self = do
1404− unsubscribe $ pushSubscribedParent self
1405− writeIORef (pushSubscribedCachedSubscribed self) $ Nothing
1406−
1407−{-# INLINE subscribePushSubscribed #-}
1408−subscribePushSubscribed :: PushSubscribed a b -> Subscriber b -> IO (SubscriberListTicket b)
1409−subscribePushSubscribed subscribed sub = WeakBag.insert sub (pushSubscribedSubscribers subscribed) (pushSubscribedWeakSelf subscribed) cleanupPushSubscribed
1410−
1411−{-# SPECIALIZE getMergeSubscribed :: GCompare k => Merge k -> Subscriber (DMap k Identity) -> EventM x (SubscriberListTicket (DMap k Identity), MergeSubscribed k) #-}
1412−getMergeSubscribed :: forall x k m. (CanSubscribe x m, GCompare k) => Merge k -> Subscriber (DMap k Identity) -> m (SubscriberListTicket (DMap k Identity), MergeSubscribed k)
1413−getMergeSubscribed m sub = do
1414− mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
1415− case mSubscribed of
1416− Just subscribed -> {-# SCC "hitMerge" #-} do
1417− sln <- liftIO $ subscribeMergeSubscribed subscribed sub
1418− return (sln, subscribed)
1419− Nothing -> {-# SCC "missMerge" #-} do
1420− subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
1421− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1422− initialParents <- readBehaviorUntracked $ current $ mergeParents m
1423− subscribers :: [(Maybe (DSum k Identity), Height, DSum k (MergeSubscribedParent k))] <- forM (DMap.toList initialParents) $ {-# SCC "getMergeSubscribed.a" #-} \(k :=> e) -> do
1424− s <- {-# SCC "getMergeSubscribed.a.1" #-} liftIO $ newSubscriberMerge k subscribedUnsafe
1425− subscription@(EventSubscription _ parentSubd) <- {-# SCC "getMergeSubscribed.a.2" #-} subscribe e s
1426− parentOcc <- {-# SCC "getMergeSubscribed.a.3" #-} liftIO $ readEventSubscribed parentSubd
1427− height <- {-# SCC "getMergeSubscribed.a.4" #-} liftIO $ getEventSubscribedHeight parentSubd
1428− {-# SCC "getMergeSubscribed.a.5" #-} return $ (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription s)
1429− let dm = DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1430− heights = fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1431− myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1432− myHeight = if invalidHeight `elem` heights
1433− then invalidHeight
1434− else succHeight $ heightBagMax myHeightBag
1435− currentHeight <- getCurrentHeight
1436− let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1437− then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1438− else (Nothing, dm)
1439− unless (DMap.null accum) $ do
1440− scheduleMerge myHeight subscribedUnsafe
1441− occRef <- liftIO $ newIORef occ
1442− when (isJust occ) $ scheduleClear occRef
1443− accumRef <- liftIO $ newIORef accum
1444− heightRef <- liftIO $ newIORef myHeight
1445− heightMapRef <- liftIO $ newIORef myHeightBag
1446− weakSelf <- liftIO $ newIORef $ error "getMergeSubscribed: weakSelf not yet initialized"
1447− (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupMergeSubscribed
1448− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1449− parentsRef <- liftIO $ newIORef $ DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1450− let !subscribed = MergeSubscribed
1451− { mergeSubscribedCachedSubscribed = mergeSubscribed m
1452− , mergeSubscribedOccurrence = occRef
1453− , mergeSubscribedAccum = accumRef
1454− , mergeSubscribedHeight = heightRef
1455− , mergeSubscribedHeightBag = heightMapRef
1456− , mergeSubscribedSubscribers = subs
1457− , mergeSubscribedChange = changeSubdRef
1458− , mergeSubscribedParents = parentsRef
1459− , mergeSubscribedWeakSelf = weakSelf
1460−#ifdef DEBUG_NODEIDS
1461− , mergeSubscribedNodeId = unsafeNodeId m
1462−#endif
1463− }
1464− defer $ SomeMergeInit subscribed $ updated $ mergeParents m
1465− liftIO $ writeIORef subscribedRef $! subscribed
1466− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "MergeSubscribed"
1467− liftIO $ writeIORef (mergeSubscribed m) $ Just subscribed
1468− return (sln, subscribed)
1469−
1470−cleanupMergeSubscribed :: MergeSubscribed k -> IO ()
1471−cleanupMergeSubscribed subscribed = do
1472− parents <- readIORef $ mergeSubscribedParents subscribed
1473− forM_ (DMap.toList parents) $ \(_ :=> msp) -> unsubscribe $ mergeSubscribedParentSubscription msp
1474− -- Not necessary, because this whole MergeSubscribed is dead: writeIORef (mergeSubscribedParents subscribed) DMap.empty
1475− writeIORef (mergeSubscribedCachedSubscribed subscribed) $ Nothing -- Get rid of the cached subscribed
1476−
1477−{-# INLINE subscribeMergeSubscribed #-}
1478−subscribeMergeSubscribed :: MergeSubscribed k -> Subscriber (DMap k Identity) -> IO (SubscriberListTicket (DMap k Identity))
1479−subscribeMergeSubscribed subscribed sub = WeakBag.insert sub (mergeSubscribedSubscribers subscribed) (mergeSubscribedWeakSelf subscribed) cleanupMergeSubscribed
1480−
1481−{-# SPECIALIZE getFanSubscribed :: GCompare k => k a -> Fan k -> Subscriber a -> EventM x (SubscriberListTicket a, FanSubscribed k) #-}
1482−getFanSubscribed :: (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderTimeline x m, GCompare k) => k a -> Fan k -> Subscriber a -> m (SubscriberListTicket a, FanSubscribed k)
1349+{-# INLINABLE getFanSubscribed #-}
1350+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)
14831351getFanSubscribed k f sub = do
14841352 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
14851353 case mSubscribed of
1486− Just subscribed -> {-# SCC "hitFan" #-} do
1487− sln <- liftIO $ subscribeFanSubscribed k subscribed sub
1488− return (sln, subscribed)
1354+ Just subscribed -> {-# SCC "hitFan" #-} liftIO $ do
1355+ sln <- subscribeFanSubscribed k subscribed sub
1356+ occ <- readIORef $ fanSubscribedOccurrence subscribed
1357+ return (sln, subscribed, coerce $ DMap.lookup k =<< occ)
14891358 Nothing -> {-# SCC "missFan" #-} do
14901359 subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
14911360 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
14921361 s <- liftIO $ newSubscriberFan subscribedUnsafe
1493− subscription <- subscribe (fanParent f) s
1362+ (subscription, parentOcc) <- subscribeAndRead (fanParent f) s
14941363 weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
14951364 (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
14961365 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1366+ occRef <- liftIO $ newIORef parentOcc
1367+ when (isJust parentOcc) $ scheduleClear occRef
14971368 let subscribed = FanSubscribed
14981369 { fanSubscribedCachedSubscribed = fanSubscribed f
1370+ , fanSubscribedOccurrence = occRef
14991371 , fanSubscribedParent = subscription
15001372 , fanSubscribedSubscribers = subscribersRef
15011373#ifdef DEBUG_NODEIDS
@@ -1507,9 +1379,9 @@ getFanSubscribed k f sub = do
15071379 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
15081380 liftIO $ writeIORef subscribedRef $! subscribed
15091381 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1510− return (slnForSub, subscribed)
1382+ return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
15111383
1512−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed k) -> IO ()
1384+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
15131385cleanupFanSubscribed (k, subscribed) = do
15141386 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15151387 let reducedSubscribers = DMap.delete k subscribers
@@ -1521,7 +1393,7 @@ cleanupFanSubscribed (k, subscribed) = do
15211393 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
15221394
15231395{-# INLINE subscribeFanSubscribed #-}
1524−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed k -> Subscriber a -> IO (SubscriberListTicket a)
1396+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
15251397subscribeFanSubscribed k subscribed sub = do
15261398 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15271399 case DMap.lookup k subscribers of
@@ -1529,18 +1401,19 @@ subscribeFanSubscribed k subscribed sub = do
15291401 let !self = (k, subscribed)
15301402 weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
15311403 (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1532− writeIORef (fanSubscribedSubscribers subscribed) $! DMap.insertWith (error "subscribeEventSubscribed: key that we just failed to find is present - should be impossible") k (FanSubscribedChildren list self weakSelf) subscribers
1404+ 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
15331405 return sln
15341406 Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
15351407
1536−{-# SPECIALIZE getSwitchSubscribed :: Switch a -> Subscriber a -> EventM x (SubscriberListTicket a, SwitchSubscribed a) #-}
1537−getSwitchSubscribed :: (Defer SomeHoldInit m, Defer SomeAssignment m, Defer SomeMergeInit m, Defer SomeClear m, Defer SomeResetCoincidence m, HasCurrentHeight m, HasSpiderTimeline x m) => Switch a -> Subscriber a -> m (SubscriberListTicket a, SwitchSubscribed a)
1408+{-# INLINABLE getSwitchSubscribed #-}
1409+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)
15381410getSwitchSubscribed s sub = do
15391411 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
15401412 case mSubscribed of
1541− Just subscribed -> {-# SCC "hitSwitch" #-} do
1542− sln <- liftIO $ subscribeSwitchSubscribed subscribed sub
1543− return (sln, subscribed)
1413+ Just subscribed -> {-# SCC "hitSwitch" #-} liftIO $ do
1414+ sln <- subscribeSwitchSubscribed subscribed sub
1415+ occ <- readIORef $ switchSubscribedOccurrence subscribed
1416+ return (sln, subscribed, occ)
15441417 Nothing -> {-# SCC "missSwitch" #-} do
15451418 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
15461419 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
@@ -1551,12 +1424,11 @@ getSwitchSubscribed s sub = do
15511424 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
15521425 holdInits <- getDeferralQueue
15531426 e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1554− subscription@(EventSubscription _ subd) <- subscribe e mySub
1427+ (subscription@(EventSubscription _ subd), parentOcc) <- subscribeAndRead e mySub
1428+ heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
15551429 subscriptionRef <- liftIO $ newIORef subscription
1556− parentOcc <- liftIO $ readEventSubscribed subd
15571430 occRef <- liftIO $ newIORef parentOcc
15581431 when (isJust parentOcc) $ scheduleClear occRef
1559− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
15601432 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
15611433 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
15621434 let !subscribed = SwitchSubscribed
@@ -1577,32 +1449,32 @@ getSwitchSubscribed s sub = do
15771449 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
15781450 liftIO $ writeIORef subscribedRef $! subscribed
15791451 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1580− return (slnForSub, subscribed)
1452+ return (slnForSub, subscribed, parentOcc)
15811453
1582−cleanupSwitchSubscribed :: SwitchSubscribed a -> IO ()
1454+cleanupSwitchSubscribed :: SwitchSubscribed x a -> IO ()
15831455cleanupSwitchSubscribed subscribed = do
15841456 unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
15851457 finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
15861458 writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
15871459
15881460{-# INLINE subscribeSwitchSubscribed #-}
1589−subscribeSwitchSubscribed :: SwitchSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1461+subscribeSwitchSubscribed :: SwitchSubscribed x a -> Subscriber x a -> IO WeakBagTicket
15901462subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
15911463
1592−{-# SPECIALIZE getCoincidenceSubscribed :: Coincidence a -> Subscriber a -> EventM x (SubscriberListTicket a, CoincidenceSubscribed a) #-}
1593−getCoincidenceSubscribed :: forall x a m. (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderTimeline x m) => Coincidence a -> Subscriber a -> m (SubscriberListTicket a, CoincidenceSubscribed a)
1464+{-# INLINABLE getCoincidenceSubscribed #-}
1465+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)
15941466getCoincidenceSubscribed c sub = do
15951467 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
15961468 case mSubscribed of
1597− Just subscribed -> {-# SCC "hitCoincidence" #-} do
1598− sln <- liftIO $ subscribeCoincidenceSubscribed subscribed sub
1599− return (sln, subscribed)
1469+ Just subscribed -> {-# SCC "hitCoincidence" #-} liftIO $ do
1470+ sln <- subscribeCoincidenceSubscribed subscribed sub
1471+ occ <- readIORef $ coincidenceSubscribedOccurrence subscribed
1472+ return (sln, subscribed, occ)
16001473 Nothing -> {-# SCC "missCoincidence" #-} do
16011474 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
16021475 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
16031476 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1604− outerSubscription@(EventSubscription _ outerSubd) <- subscribe (coincidenceParent c) subOuter
1605− outerOcc <- liftIO $ readEventSubscribed outerSubd
1477+ (outerSubscription@(EventSubscription _ outerSubd), outerOcc) <- subscribeAndRead (coincidenceParent c) subOuter
16061478 outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
16071479 (occ, height, mInnerSubd) <- case outerOcc of
16081480 Nothing -> return (Nothing, outerHeight, Nothing)
@@ -1632,35 +1504,158 @@ getCoincidenceSubscribed c sub = do
16321504 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
16331505 liftIO $ writeIORef subscribedRef $! subscribed
16341506 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1635− return (slnForSub, subscribed)
1507+ return (slnForSub, subscribed, occ)
16361508
1637−cleanupCoincidenceSubscribed :: CoincidenceSubscribed a -> IO ()
1509+cleanupCoincidenceSubscribed :: CoincidenceSubscribed x a -> IO ()
16381510cleanupCoincidenceSubscribed subscribed = do
16391511 unsubscribe $ coincidenceSubscribedOuterParent subscribed
16401512 writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
16411513
16421514{-# INLINE subscribeCoincidenceSubscribed #-}
1643−subscribeCoincidenceSubscribed :: CoincidenceSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1515+subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
16441516subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
16451517
16461518{-# INLINE merge #-}
1647−merge :: GCompare k => Dynamic R.PatchDMap (DMap k Event) -> Event (DMap k Identity)
1648−merge m = EventMerge $ Merge
1649− { mergeParents = m
1650− , mergeSubscribed = unsafeNewIORef m Nothing
1651− }
1652−
1653−newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
1654−
1655−fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
1519+merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1520+merge d = cacheEvent (mergeCheap d)
1521+
1522+{-# INLINE [1] mergeCheap #-}
1523+mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1524+mergeCheap d = Event $ \sub -> do
1525+ initialParents <- readBehaviorUntracked $ dynamicCurrent d
1526+ accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1527+ heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1528+ heightBagRef <- liftIO $ newIORef $ error "merge: heightBagRef not yet initialized"
1529+ parentsRef <- liftIO $ newIORef $ error "merge: parentsRef not yet initialized"
1530+ let scheduleSelf height = scheduleMerge' height heightRef $ do
1531+ m <- liftIO $ readIORef accumRef
1532+ liftIO $ writeIORef accumRef $! DMap.empty
1533+ --TODO: Assert that m is not empty
1534+ subscriberPropagate sub m
1535+ invalidateMyHeight = do
1536+ oldHeight <- readIORef heightRef
1537+ 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
1538+ writeIORef heightRef $! invalidHeight
1539+ subscriberInvalidateHeight sub oldHeight
1540+ revalidateMyHeight = do
1541+ currentHeight <- readIORef heightRef
1542+ when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1543+ heights <- readIORef heightBagRef
1544+ parents <- readIORef parentsRef
1545+ -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1546+ case heightBagSize heights `compare` DMap.size parents of
1547+ LT -> return ()
1548+ EQ -> do
1549+ let height = succHeight $ heightBagMax heights
1550+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1551+ writeIORef heightRef $! height
1552+ subscriberRecalculateHeight sub height
1553+ GT -> error $ "revalidateMergeHeight: more heights than parents for Merge"
1554+ subscriber :: forall a. k a -> Subscriber x a
1555+ subscriber k = Subscriber
1556+ { subscriberPropagate = \a -> do
1557+ oldM <- liftIO $ readIORef accumRef
1558+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1559+ tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1560+ liftIO $ writeIORef accumRef $! newM
1561+ when (DMap.null oldM) $ do -- Only schedule the firing once
1562+ height <- liftIO $ readIORef heightRef
1563+ --TODO: assertions about height
1564+ currentHeight <- getCurrentHeight
1565+ when (height <= currentHeight) $ do
1566+ if height /= invalidHeight
1567+ then do
1568+ myStack <- liftIO $ whoCreatedIORef undefined --TODO
1569+ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1570+ else liftIO $ do
1571+#ifdef DEBUG_CYCLES
1572+ nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1573+ stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1574+ let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1575+#else
1576+ let cycleInfo = ""
1577+#endif
1578+ error $ "Causality loop found" <> cycleInfo
1579+ scheduleSelf height
1580+ , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1581+ modifyIORef' heightBagRef $ heightBagRemove old
1582+ invalidateMyHeight
1583+ , subscriberRecalculateHeight = \new -> do
1584+ modifyIORef' heightBagRef $ heightBagAdd new
1585+ revalidateMyHeight
1586+ }
1587+ subscribers :: [(Maybe (DSum k Identity), Height, DSum k (MergeSubscribedParent x))] <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1588+ let s = subscriber k
1589+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1590+ height <- liftIO $ getEventSubscribedHeight parentSubd
1591+ return $ (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1592+ let dm = DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1593+ heights = fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1594+ myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1595+ myHeight = if invalidHeight `elem` heights
1596+ then invalidHeight
1597+ else succHeight $ heightBagMax myHeightBag
1598+ currentHeight <- getCurrentHeight
1599+ let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1600+ then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1601+ else (Nothing, dm)
1602+ unless (DMap.null accum) $ do
1603+ scheduleSelf myHeight
1604+ occRef <- liftIO $ newIORef occ
1605+ when (isJust occ) $ scheduleClear occRef
1606+ liftIO $ writeIORef accumRef $! accum
1607+ liftIO $ writeIORef heightRef $! myHeight
1608+ liftIO $ writeIORef heightBagRef $! myHeightBag
1609+ changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1610+ liftIO $ writeIORef parentsRef $! DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1611+ let updateMe (PatchDMap p) = do
1612+ oldParents <- liftIO $ readIORef parentsRef
1613+ let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1614+ (mOldSubd, newPs) <- case me of
1615+ Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1616+ Just e -> do
1617+ let s = subscriber k
1618+ subscription@(EventSubscription _ subd) <- subscribe e s
1619+ newParentHeight <- liftIO $ getEventSubscribedHeight subd
1620+ let newParent = MergeSubscribedParent subscription
1621+ liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1622+ return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1623+ forM_ mOldSubd $ \oldSubd -> do
1624+ oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ unMergeSubscribedParent oldSubd
1625+ liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1626+ return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1627+ (subscriptionsToKill, newParents) <- foldM f ([], oldParents) $ DMap.toList p --TODO: I think this runEventM is OK, since no events are firing at this time, but it might not be
1628+ liftIO $ writeIORef parentsRef $! newParents
1629+ return subscriptionsToKill
1630+ defer $ SomeMergeInit $ do
1631+ let s = Subscriber
1632+ { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
1633+ tracePropagate (Proxy :: Proxy x) $ "SubscriberMerge/Change"
1634+ defer $ SomeMergeUpdate (updateMe a) invalidateMyHeight revalidateMyHeight
1635+ , subscriberInvalidateHeight = \_ -> return ()
1636+ , subscriberRecalculateHeight = \_ -> return ()
1637+ }
1638+ (EventSubscription _ changeSubd, change) <- subscribeAndRead (dynamicUpdated d) s
1639+ forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1640+ liftIO $ writeIORef changeSubdRef (s, changeSubd)
1641+ let unsubscribeAll = do
1642+ parents <- readIORef parentsRef
1643+ forM_ (DMap.toList parents) $ \(_ :=> MergeSubscribedParent s) -> unsubscribe s
1644+ return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parentsRef, changeSubdRef)
1645+ , occ
1646+ )
1647+
1648+newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
1649+
1650+fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
16561651fan e =
16571652 let f = Fan
16581653 { fanParent = e
16591654 , fanSubscribed = unsafeNewIORef e Nothing
16601655 }
1661− in EventSelector $ \k -> EventFan k f
1656+ in EventSelector $ \k -> eventFan k f
16621657
1663−runHoldInits :: IORef [SomeHoldInit] -> IORef [SomeMergeInit] -> EventM x ()
1658+runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeMergeInit x] -> EventM x ()
16641659runHoldInits holdInitRef mergeInitRef = do
16651660 holdInits <- liftIO $ readIORef holdInitRef
16661661 mergeInits <- liftIO $ readIORef mergeInitRef
@@ -1668,26 +1663,14 @@ runHoldInits holdInitRef mergeInitRef = do
16681663 liftIO $ writeIORef holdInitRef []
16691664 liftIO $ writeIORef mergeInitRef []
16701665 mapM_ initHold holdInits
1671− mapM_ initMerge mergeInits
1666+ mapM_ unSomeMergeInit mergeInits
16721667 runHoldInits holdInitRef mergeInitRef
16731668
1674−initHold :: SomeHoldInit -> EventM x ()
1669+initHold :: HasSpiderTimeline x => SomeHoldInit x -> EventM x ()
16751670initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
16761671
1677−initMerge :: SomeMergeInit -> EventM x ()
1678−initMerge (SomeMergeInit subscribed changed) = do
1679− changeSub <- liftIO $ newSubscriberMergeChange subscribed
1680− EventSubscription _ changeSubd <- subscribe changed changeSub
1681− change <- liftIO $ readEventSubscribed changeSubd
1682− forM_ change $ \c -> defer $ SomeMergeUpdate c subscribed
1683− liftIO $ writeIORef (mergeSubscribedChange subscribed) (changeSub, changeSubd)
1684−
1685−-- | Run an event action outside of a frame
1686−runFrame :: EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1687−runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1688− -- Clear out pending unsubscriptions; these will need to happen eventually anyway, and might cause us a lot of extra work during the frame, so get rid of them now
1689− pendingCleanups <- atomicModifyIORef (_spiderEnv_cleanups spiderEnv) $ \x -> ([], x)
1690− {-# SCC "cleanups" #-} sequence_ pendingCleanups
1672+newEventEnv :: IO (EventEnv x)
1673+newEventEnv = do
16911674 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
16921675 holdInitRef <- newIORef []
16931676 mergeUpdateRef <- newIORef []
@@ -1696,50 +1679,48 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
16961679 toClearRef <- newIORef []
16971680 toClearRootRef <- newIORef []
16981681 coincidenceInfosRef <- newIORef []
1699− delayedRef <- liftIO $ newIORef IntMap.empty
1700− let env = EventEnv spiderEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1682+ delayedRef <- newIORef IntMap.empty
1683+ return $ EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1684+
1685+clearEventEnv :: EventEnv x -> IO ()
1686+clearEventEnv (EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
1687+ writeIORef toAssignRef []
1688+ writeIORef holdInitRef []
1689+ writeIORef mergeUpdateRef []
1690+ writeIORef mergeInitRef []
1691+ writeIORef heightRef zeroHeight
1692+ writeIORef toClearRef []
1693+ writeIORef toClearRootRef []
1694+ writeIORef coincidenceInfosRef []
1695+ writeIORef delayedRef IntMap.empty
1696+
1697+-- | Run an event action outside of a frame
1698+runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1699+runFrame a = SpiderHost $ ask >>= \_ -> lift $ do
1700+ let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
17011701 let go = do
17021702 result <- a
1703− runHoldInits holdInitRef mergeInitRef -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
1703+ 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
17041704 return result
1705− result <- runEventM go env
1706− toClear <- readIORef toClearRef
1705+ result <- runEventM go
1706+ toClear <- readIORef $ eventEnvClears env
17071707 forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1708− toClearRoot <- readIORef toClearRootRef
1708+ toClearRoot <- readIORef $ eventEnvRootClears env
17091709 forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1710− toAssign <- readIORef toAssignRef
1710+ toAssign <- readIORef $ eventEnvAssignments env
17111711 toReconnectRef <- newIORef []
1712− coincidenceInfos <- readIORef coincidenceInfosRef
1712+ coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
17131713 forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
17141714 writeIORef vRef v
17151715 when debugInvalidate $ putStrLn $ "Invalidating Hold"
17161716 writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1717− mergeUpdates <- readIORef mergeUpdateRef
1718− writeIORef mergeUpdateRef []
1719− let updateMerge :: SomeMergeUpdate -> IO [SomeEventSubscription]
1720− updateMerge (SomeMergeUpdate (R.PatchDMap p :: R.PatchDMap (DMap k Event)) m) = do
1721− oldParents <- readIORef $ mergeSubscribedParents m
1722− let f (subscriptionsToKill, ps) (k :=> Compose me) = do
1723− (mOldSubd, newPs) <- case me of
1724− Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1725− Just e -> do
1726− s <- liftIO $ newSubscriberMerge k m
1727− subscription@(EventSubscription _ subd) <- subscribe e s
1728− let newParent = MergeSubscribedParent subscription s
1729− newParentHeight <- liftIO $ getEventSubscribedHeight subd
1730− liftIO $ addMergeHeight newParentHeight m
1731− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1732− forM_ mOldSubd $ \oldSubd -> do
1733− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription oldSubd
1734− liftIO $ removeMergeHeight oldHeight m
1735− return (maybeToList (SomeEventSubscription . mergeSubscribedParentSubscription <$> mOldSubd) ++ subscriptionsToKill, newPs)
1736− (subscriptionsToKill, newParents) <- flip runEventM env $ foldM f ([], oldParents) $ DMap.toList p --TODO: I think this runEventM is OK, since no events are firing at this time, but it might not be
1737− writeIORef (mergeSubscribedParents m) $! newParents
1738− return subscriptionsToKill
1717+ mergeUpdates <- readIORef $ eventEnvMergeUpdates env
1718+ writeIORef (eventEnvMergeUpdates env) []
17391719 when debugPropagate $ putStrLn "Updating merges"
1740− mergeSubscriptionsToKill <- concat <$> mapM updateMerge mergeUpdates
1720+ mergeSubscriptionsToKill <- runEventM $ concat <$> mapM _someMergeUpdate_update mergeUpdates
17411721 when debugPropagate $ putStrLn "Updating merges done"
17421722 toReconnect <- readIORef toReconnectRef
1723+ clearEventEnv env
17431724 switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
17441725 oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
17451726 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
@@ -1749,12 +1730,12 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
17491730 wi' <- mkWeakPtrWithDebug i "wi'"
17501731 writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
17511732 writeIORef (switchSubscribedBehaviorParents subscribed) []
1752− writeIORef holdInitRef [] --TODO: Should we reuse this?
1753− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) holdInitRef
1754− runEventM (runHoldInits holdInitRef mergeInitRef) 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
1733+ writeIORef (eventEnvHoldInits env) [] --TODO: Should we reuse this?
1734+ e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) $ eventEnvHoldInits env
1735+ 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
17551736 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
17561737 sub <- newSubscriberSwitch subscribed
1757− subscription <- runReaderT (unSpiderHost (runFrame ({-# SCC "subscribeSwitch" #-} subscribe e sub))) spiderEnv --TODO: Assert that the event isn't firing --TODO: This should not loop because none of the events should be firing, but still, it is inefficient
1738+ 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
17581739 {-
17591740 stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
17601741 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
@@ -1763,9 +1744,9 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
17631744 _ -> "something else"
17641745 -}
17651746 writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1766− return $ SomeEventSubscription oldSubscription
1767− forM_ mergeSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1768− forM_ switchSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1747+ return oldSubscription
1748+ liftIO $ mapM_ unsubscribe mergeSubscriptionsToKill
1749+ liftIO $ mapM_ unsubscribe switchSubscriptionsToKill
17691750 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
17701751 EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
17711752 parentHeight <- getEventSubscribedHeight subd'
@@ -1773,13 +1754,12 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
17731754 when (parentHeight /= myHeight) $ do
17741755 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
17751756 WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1776− forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> do
1777− invalidateMergeHeight m --TODO: In addition to when the patch is completely empty, we should also not run this if it has some Nothing values, but none of them have actually had any effect; potentially, we could even check for Just values with no effect (e.g. by comparing their IORefs and ignoring them if they are unchanged); actually, we could just check if the new height is different
1757+ 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
17781758 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
17791759 unsubscribe subscription
17801760 mapM_ invalidateCoincidenceHeight mcs
17811761 forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1782− forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> revalidateMergeHeight m
1762+ mapM_ _someMergeUpdate_recalculateHeight mergeUpdates
17831763 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
17841764 height <- calculateSwitchHeight subscribed
17851765 updateSwitchHeight height subscribed
@@ -1795,10 +1775,12 @@ zeroHeight = Height 0
17951775invalidHeight :: Height
17961776invalidHeight = Height (-1000)
17971777
1778+#ifdef DEBUG_CYCLES
17981779-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
17991780{-# INLINE invalidHeightBeingTraversed #-}
18001781invalidHeightBeingTraversed :: Height
18011782invalidHeightBeingTraversed = Height (-1001)
1783+#endif
18021784
18031785{-# INLINE succHeight #-}
18041786succHeight :: Height -> Height
@@ -1807,121 +1789,14 @@ succHeight h@(Height a) =
18071789 then invalidHeight
18081790 else Height $ succ a
18091791
1810−invalidateSubscriberHeight :: Height -> Subscriber a -> IO ()
1811−invalidateSubscriberHeight old s = case s of
1812− SubscriberPush _ subscribed -> do
1813− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1814− WeakBag.traverse (pushSubscribedSubscribers subscribed) $ invalidateSubscriberHeight old
1815− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1816− SubscriberMerge _ subscribed -> do
1817− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1818− removeMergeHeight old subscribed
1819− invalidateMergeHeight subscribed
1820− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1821− SubscriberMergeChange _ -> return () -- Doesn't affect the height of the merge, because its changes are delayed
1822− SubscriberFan subscribed -> do
1823− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1824− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1825− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
1826− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1827− SubscriberHold _ -> return ()
1828− SubscriberHoldIdentity _ -> return ()
1829− SubscriberSwitch subscribed -> do
1830− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1831− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1832− when (oldHeight /= invalidHeight) $ do
1833− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1834− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1835− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1836− SubscriberCoincidenceOuter subscribed -> do
1837− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1838− invalidateCoincidenceHeight subscribed
1839− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1840− SubscriberCoincidenceInner subscribed -> do
1841− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1842− invalidateCoincidenceHeight subscribed
1843− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1844− SubscriberHandle -> return ()
1845−
1846−removeMergeHeight :: Height -> MergeSubscribed k -> IO ()
1847−removeMergeHeight oldParentHeight subscribed = do
1848− when debugInvalidateHeight $ putStrLn $ "removeMergeHeight: " <> show oldParentHeight <> " Merge" <> showNodeId subscribed
1849− oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1850− let newHeights = heightBagRemove oldParentHeight oldHeights
1851− writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1852−
1853−invalidateMergeHeight :: MergeSubscribed k -> IO ()
1854−invalidateMergeHeight subscribed = do
1855− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1856− -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1857− when (oldHeight /= invalidHeight) $ do
1858− writeIORef (mergeSubscribedHeight subscribed) $! invalidHeight
1859− WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1860−
1861−addMergeHeight :: Height -> MergeSubscribed k -> IO ()
1862−addMergeHeight newParentHeight subscribed = do
1863− when debugInvalidateHeight $ putStrLn $ "addMergeHeight: " <> show newParentHeight <> " Merge" <> showNodeId subscribed
1864− oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1865− let newHeights = heightBagAdd newParentHeight oldHeights
1866− writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1867−
1868−revalidateMergeHeight :: MergeSubscribed k -> IO ()
1869−revalidateMergeHeight subscribed = do
1870− currentHeight <- readIORef $ mergeSubscribedHeight subscribed
1871− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1872− heights <- readIORef $ mergeSubscribedHeightBag subscribed
1873− parents <- readIORef $ mergeSubscribedParents subscribed
1874− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1875− case heightBagSize heights `compare` DMap.size parents of
1876− LT -> return ()
1877− EQ -> do
1878− let height = succHeight $ heightBagMax heights
1879− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1880− writeIORef (mergeSubscribedHeight subscribed) $! height
1881− WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1882− GT -> error $ "revalidateMergeHeight: more heights than parents for Merge" <> showNodeId subscribed
1883−
1884−invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1792+invalidateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
18851793invalidateCoincidenceHeight subscribed = do
18861794 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
18871795 when (oldHeight /= invalidHeight) $ do
18881796 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
18891797 WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
18901798
1891−recalculateSubscriberHeight :: Height -> Subscriber a -> IO ()
1892−recalculateSubscriberHeight new s = case s of
1893− SubscriberPush _ subscribed -> do
1894− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1895− WeakBag.traverse (pushSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1896− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1897− SubscriberMerge _ subscribed -> do
1898− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1899− addMergeHeight new subscribed
1900− revalidateMergeHeight subscribed
1901− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1902− SubscriberMergeChange _ -> return ()
1903− SubscriberFan subscribed -> do
1904− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1905− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1906− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
1907− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1908− SubscriberHold _ -> return ()
1909− SubscriberHoldIdentity _ -> return ()
1910− SubscriberSwitch subscribed -> do
1911− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1912− updateSwitchHeight new subscribed
1913− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1914− SubscriberCoincidenceOuter subscribed -> do
1915− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1916− recalculateCoincidenceHeight subscribed
1917− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1918− SubscriberCoincidenceInner subscribed -> do
1919− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1920− recalculateCoincidenceHeight subscribed
1921− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1922− SubscriberHandle -> return ()
1923−
1924−updateSwitchHeight :: Height -> SwitchSubscribed a -> IO ()
1799+updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
19251800updateSwitchHeight new subscribed = do
19261801 oldHeight <- readIORef $ switchSubscribedHeight subscribed
19271802 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
@@ -1929,7 +1804,7 @@ updateSwitchHeight new subscribed = do
19291804 writeIORef (switchSubscribedHeight subscribed) $! new
19301805 WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
19311806
1932−recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1807+recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
19331808recalculateCoincidenceHeight subscribed = do
19341809 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
19351810 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
@@ -1938,18 +1813,18 @@ recalculateCoincidenceHeight subscribed = do
19381813 writeIORef (coincidenceSubscribedHeight subscribed) $! height
19391814 WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
19401815
1941−calculateSwitchHeight :: SwitchSubscribed a -> IO Height
1816+calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
19421817calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
19431818
1944−calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Height
1819+calculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO Height
19451820calculateCoincidenceHeight subscribed = do
19461821 outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
19471822 innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
19481823 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
19491824
1950−data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed a)
1825+data SomeSwitchSubscribed x = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed x a)
19511826
1952−invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1827+invalidate :: IORef [SomeSwitchSubscribed x] -> WeakList (Invalidator x) -> IO (WeakList (Invalidator x))
19531828invalidate toReconnectRef wis = do
19541829 forM_ wis $ \wi -> do
19551830 mi <- deRefWeak wi
@@ -1975,174 +1850,95 @@ invalidate toReconnectRef wis = do
19751850-- Reflex integration
19761851--------------------------------------------------------------------------------
19771852
1978−-- | The default, global Spider environment
1979−type Spider = SpiderTimeline Global
1980−
1981−-- | Designates the default, global Spider timeline
1982−data Global
1983−
1984−{-# NOINLINE globalSpiderTimeline #-}
1985−globalSpiderTimeline :: SpiderTimeline Global
1986−globalSpiderTimeline = unsafePerformIO unsafeNewSpiderTimeline
1987−
1988−-- | Stores all global data relevant to a particular Spider timeline; only one
1989−-- value should exist for each type @x@
1990−data SpiderTimeline x = SpiderTimeline
1991− { _spiderEnv_toUnsubscribe :: !(IORef [SomeEventSubscription])
1992− , _spiderEnv_lock :: !(MVar ())
1993− , _spiderEnv_cleanups :: !(IORef [IO ()])
1994−#ifdef DEBUG
1995− , _spiderEnv_depth :: !(IORef Int)
1853+#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1854+-- When we're specializing, we include this from Reflex.Class instead, because
1855+-- it needs access to these types; when we're not, we want them here, so our
1856+-- instance isn't an orphan instance.
1857+#include "SpiderTimeline.include.hs"
1858+#include "SpiderTimelineHost.include.hs"
19961859#endif
1997− }
1998−type role SpiderTimeline nominal
19991860
2000−instance GEq SpiderTimeline where
2001− a `geq` b = if _spiderEnv_toUnsubscribe a == _spiderEnv_toUnsubscribe b
2002− then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimeline can't have two different 'x' arguments
2003− else Nothing
2004−
2005−unsafeNewSpiderTimeline :: forall x. IO (SpiderTimeline x)
2006−unsafeNewSpiderTimeline = do
2007− toUnsubscribe <- newIORef []
1861+unsafeNewSpiderTimelineEnv :: forall x. IO (SpiderTimelineEnv x)
1862+unsafeNewSpiderTimelineEnv = do
20081863 lock <- newMVar ()
2009− cleanups <- newIORef []
1864+ env <- newEventEnv
20101865#ifdef DEBUG
20111866 depthRef <- newIORef 0
20121867#endif
2013− return $ SpiderTimeline
2014− { _spiderEnv_toUnsubscribe = toUnsubscribe
2015− , _spiderEnv_lock = lock
2016− , _spiderEnv_cleanups = cleanups
1868+ return $ SpiderTimelineEnv
1869+ { _spiderTimeline_lock = lock
1870+ , _spiderTimeline_eventEnv = env
20171871#ifdef DEBUG
2018− , _spiderEnv_depth = depthRef
1872+ , _spiderTimeline_depth = depthRef
20191873#endif
20201874 }
20211875
2022−-- | Create a new SpiderTimeline
2023−newSpiderTimeline :: IO (Some SpiderTimeline)
1876+-- | Create a new SpiderTimelineEnv
1877+newSpiderTimeline :: IO (Some SpiderTimelineEnv)
20241878newSpiderTimeline = withSpiderTimeline (pure . Some.This)
20251879
20261880-- | Pass a new timeline to the given function.
2027−withSpiderTimeline :: (forall x. SpiderTimeline x -> IO r) -> IO r
2028−withSpiderTimeline k = unsafeNewSpiderTimeline >>= k
1881+withSpiderTimeline :: (forall x. SpiderTimelineEnv x -> IO r) -> IO r
1882+withSpiderTimeline k = unsafeNewSpiderTimelineEnv >>= k
20291883
2030−newtype SpiderPullM x a = SpiderPullM (BehaviorM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
1884+newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
20311885
2032−newtype SpiderPushM x a = SpiderPushM (ComputeM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
1886+newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
20331887
2034−instance R.Reflex (SpiderTimeline x) where
2035− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior a }
2036− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event a }
2037− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic Identity a } deriving (Functor, Applicative, Monad)
2038− newtype Incremental (SpiderTimeline x) p a = SpiderIncremental { unSpiderIncremental :: Dynamic p a }
1888+#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1889+instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
1890+ {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
1891+ newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
1892+ newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
1893+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
1894+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
20391895 type PullM (SpiderTimeline x) = SpiderPullM x
20401896 type PushM (SpiderTimeline x) = SpiderPushM x
20411897 {-# INLINABLE never #-}
2042− never = SpiderEvent EventNever
1898+ never = SpiderEvent eventNever
20431899 {-# INLINABLE constant #-}
2044− constant = SpiderBehavior . BehaviorConst
2045− {-# INLINABLE push #-}
1900+ constant = SpiderBehavior . behaviorConst
1901+ {-# INLINE push #-}
20461902 push f = SpiderEvent . push (coerce f) . unSpiderEvent
20471903 {-# INLINABLE pull #-}
20481904 pull = SpiderBehavior . pull . coerce
20491905 {-# INLINABLE merge #-}
2050− merge = SpiderEvent . merge . DynamicConst . (unsafeCoerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k Event)
1906+ merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
20511907 {-# INLINABLE fan #-}
20521908 fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
20531909 {-# INLINABLE switch #-}
2054− switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event (SpiderTimeline x) a) -> Behavior (Event a)) . unSpiderBehavior
1910+ switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
20551911 {-# INLINABLE coincidence #-}
2056− coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event (SpiderTimeline x) a) -> Event (Event a)) . unSpiderEvent
1912+ coincidence = SpiderEvent . coincidence . (coerce :: Event x (R.Event (SpiderTimeline x) a) -> Event x (Event x a)) . unSpiderEvent
20571913 {-# INLINABLE current #-}
2058− current = SpiderBehavior . current . unSpiderDynamic
1914+ current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
20591915 {-# INLINABLE updated #-}
2060− updated = SpiderEvent . fmap runIdentity . updated . unSpiderDynamic
1916+ updated = SpiderEvent . fmap runIdentity . dynamicUpdated . unSpiderDynamic
20611917 {-# INLINABLE unsafeBuildDynamic #-}
2062− unsafeBuildDynamic readV0 v' = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (coerce readV0) $ addIdentity $ unSpiderEvent v'
1918+ unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
20631919 {-# INLINABLE unsafeBuildIncremental #-}
2064− unsafeBuildIncremental readV0 dv = SpiderIncremental $ DynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
1920+ unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
20651921 {-# INLINABLE mergeIncremental #-}
2066− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic R.PatchDMap (DMap k (R.Event (SpiderTimeline x))) -> Dynamic R.PatchDMap (DMap k Event)) . unSpiderIncremental
1922+ mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
20671923 {-# INLINABLE currentIncremental #-}
2068− currentIncremental = SpiderBehavior . current . unSpiderIncremental
1924+ currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
20691925 {-# INLINABLE updatedIncremental #-}
2070− updatedIncremental = SpiderEvent . updated . unSpiderIncremental
1926+ updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
20711927 {-# INLINABLE incrementalToDynamic #-}
2072− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ current i) $ flip push (updated i) $ \p -> do
2073− c <- readBehaviorUntracked $ current i
2074− return $ Identity <$> R.apply p c --TODO: Avoid the redundant 'apply'
2075−
2076−instance R.MonadSample (SpiderTimeline x) (EventM x) where
2077− {-# INLINABLE sample #-}
2078− sample (SpiderBehavior b) = readBehaviorUntracked b
2079−
2080−instance R.MonadHold (SpiderTimeline x) (EventM x) where
2081− {-# INLINABLE hold #-}
2082− hold = holdSpiderEventM
2083− {-# INLINABLE holdDyn #-}
2084− holdDyn = holdDynSpiderEventM
2085− {-# INLINABLE holdIncremental #-}
2086− holdIncremental = holdIncrementalSpiderEventM
2087−
2088−holdSpiderEventM :: a -> R.Event (SpiderTimeline x) a -> EventM x (R.Behavior (SpiderTimeline x) a)
2089−holdSpiderEventM v0 e = fmap (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2090−
2091−holdDynSpiderEventM :: a -> R.Event (SpiderTimeline x) a -> EventM x (R.Dynamic (SpiderTimeline x) a)
2092−holdDynSpiderEventM v0 e = fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2093−
2094−holdIncrementalSpiderEventM :: R.Patch p => a -> R.Event (SpiderTimeline x) (p a) -> EventM x (R.Incremental (SpiderTimeline x) p a)
2095−holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
2096−
2097−instance R.MonadHold (SpiderTimeline x) (SpiderHost x) where
2098− {-# INLINABLE hold #-}
2099− hold v0 e = R.runHostFrame $ R.hold v0 e
2100− {-# INLINABLE holdDyn #-}
2101− holdDyn v0 e = R.runHostFrame $ R.holdDyn v0 e
2102− {-# INLINABLE holdIncremental #-}
2103− holdIncremental v0 e = R.runHostFrame $ R.holdIncremental v0 e
2104−
2105−instance R.MonadSample (SpiderTimeline x) (SpiderHost x) where
2106− {-# INLINABLE sample #-}
2107− sample = runFrame . readBehaviorUntracked . unSpiderBehavior
2108−
2109−instance R.MonadSample (SpiderTimeline x) (SpiderPullM x) where
2110− {-# INLINABLE sample #-}
2111− sample = coerce . readBehaviorTracked . unSpiderBehavior
2112−
2113−instance R.MonadSample (SpiderTimeline x) (SpiderPushM x) where
2114− {-# INLINABLE sample #-}
2115− sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
2116−
2117−instance R.MonadHold (SpiderTimeline x) (SpiderPushM x) where
2118− {-# INLINABLE hold #-}
2119− hold v0 e = R.current <$> R.holdDyn v0 e
2120− {-# INLINABLE holdDyn #-}
2121− holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity e
2122− {-# INLINABLE holdIncremental #-}
2123− holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . DynamicHold <$> hold v0 e
2124−
2125−data RootTrigger a = forall k. GCompare k => RootTrigger (SubscriberList a, IORef (DMap k Identity), k a)
2126−newtype SpiderEventHandle a = SpiderEventHandle { unSpiderEventHandle :: EventSubscription a }
2127−
2128−instance R.MonadSubscribeEvent (SpiderTimeline x) (SpiderHostFrame x) where
2129− {-# INLINABLE subscribeEvent #-}
2130− subscribeEvent e = SpiderHostFrame $ do
2131− subscription <- subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2132− return $ SpiderEventHandle subscription
2133−
2134−instance R.ReflexHost (SpiderTimeline x) where
2135− type EventTrigger (SpiderTimeline x) = RootTrigger
2136− type EventHandle (SpiderTimeline x) = SpiderEventHandle
2137− type HostFrame (SpiderTimeline x) = SpiderHostFrame x
2138−
2139−instance R.MonadReadEvent (SpiderTimeline x) (ReadPhase x) where
2140− {-# INLINABLE readEvent #-}
2141− readEvent h = ReadPhase $ fmap (fmap return) $ liftIO $ do
2142− let EventSubscription sln subscribed = unSpiderEventHandle h
2143− result <- readEventSubscribed subscribed
2144− touch sln
2145− return result
1928+ incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip push (dynamicUpdated i) $ \p -> do
1929+ c <- readBehaviorUntracked $ dynamicCurrent i
1930+ return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
1931+ eventCoercion Coercion = Coercion
1932+ behaviorCoercion Coercion = Coercion
1933+ dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
1934+#endif
1935+
1936+data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)
1937+
1938+data SpiderEventHandle x a = SpiderEventHandle
1939+ { spiderEventHandleSubscription :: EventSubscription x
1940+ , spiderEventHandleValue :: IORef (Maybe a)
1941+ }
21461942
21471943instance MonadRef (EventM x) where
21481944 type Ref (EventM x) = Ref IO
@@ -2158,7 +1954,7 @@ instance MonadAtomicRef (EventM x) where
21581954 atomicModifyRef r f = liftIO $ atomicModifyRef r f
21591955
21601956-- | The monad for actions that manipulate a Spider timeline identified by @x@
2161−newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderTimeline x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
1957+newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderTimelineEnv x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
21621958
21631959instance Monad (SpiderHost x) where
21641960 {-# INLINABLE (>>=) #-}
@@ -2173,11 +1969,11 @@ instance Monad (SpiderHost x) where
21731969-- | Run an action affecting the global Spider timeline; this will be guarded by
21741970-- a mutex for that timeline
21751971runSpiderHost :: SpiderHost Global a -> IO a
2176−runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimeline
1972+runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimelineEnv
21771973
21781974-- | Run an action affecting a given Spider timeline; this will be guarded by a
21791975-- mutex for that timeline
2180−runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimeline x -> IO a
1976+runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
21811977runSpiderHostForTimeline (SpiderHost a) = runReaderT a
21821978
21831979newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
@@ -2192,27 +1988,12 @@ instance Monad (SpiderHostFrame x) where
21921988 {-# INLINABLE fail #-}
21931989 fail s = SpiderHostFrame $ fail s
21941990
2195−instance R.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
2196− sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
2197−
2198−addIdentity :: Event a -> Event (Identity a)
2199−--addIdentity = fmap Identity
2200−addIdentity = unsafeCoerce
2201−
2202−instance R.MonadHold (SpiderTimeline x) (SpiderHostFrame x) where
2203− {-# INLINABLE hold #-}
2204− hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2205− {-# INLINABLE holdDyn #-}
2206− holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2207− {-# INLINABLE holdIncremental #-}
2208− holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
2209−
2210−newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
1991+newEventWithTriggerIO :: (RootTrigger x a -> IO (IO ())) -> IO (Event x a)
22111992newEventWithTriggerIO f = do
22121993 es <- newFanEventWithTriggerIO $ \Refl -> f
22131994 return $ select es Refl
22141995
2215−newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger a -> IO (IO ())) -> IO (EventSelector k)
1996+newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger x a -> IO (IO ())) -> IO (EventSelector x k)
22161997newFanEventWithTriggerIO f = do
22171998 occRef <- newIORef DMap.empty
22181999 subscribedRef <- newIORef DMap.empty
@@ -2221,48 +2002,10 @@ newFanEventWithTriggerIO f = do
22212002 , rootSubscribed = subscribedRef
22222003 , rootInit = f
22232004 }
2224− return $ EventSelector $ \k -> EventRoot k r
2225−
2226−instance R.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHost x) where
2227− newEventWithTrigger = SpiderHost . lift . fmap SpiderEvent . newEventWithTriggerIO
2228− newFanEventWithTrigger f = SpiderHost $ lift $ do
2229− es <- newFanEventWithTriggerIO f
2230− return $ R.EventSelector $ SpiderEvent . select es
2231−
2232−instance R.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHostFrame x) where
2233− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
2234− newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
2235− es <- newFanEventWithTriggerIO f
2236− return $ R.EventSelector $ SpiderEvent . select es
2237−
2238−instance R.MonadSubscribeEvent (SpiderTimeline x) (SpiderHost x) where
2239− {-# INLINABLE subscribeEvent #-}
2240− subscribeEvent = subscribeEventSpiderHost
2241−
2242−subscribeEventSpiderHost :: R.Event (SpiderTimeline x) a -> SpiderHost x (R.EventHandle (SpiderTimeline x) a)
2243−subscribeEventSpiderHost e = do
2244− subscription <- runFrame $ subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2245− return $ SpiderEventHandle subscription
2005+ return $ EventSelector $ \k -> eventRoot k r
22462006
22472007newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
22482008
2249−instance R.MonadSample (SpiderTimeline x) (ReadPhase x) where
2250− {-# INLINABLE sample #-}
2251− sample = ReadPhase . R.sample
2252−
2253−instance R.MonadHold (SpiderTimeline x) (ReadPhase x) where
2254− {-# INLINABLE hold #-}
2255− hold v0 e = ReadPhase $ R.hold v0 e
2256− {-# INLINABLE holdDyn #-}
2257− holdDyn v0 e = ReadPhase $ R.holdDyn v0 e
2258− {-# INLINABLE holdIncremental #-}
2259− holdIncremental v0 e = ReadPhase $ R.holdIncremental v0 e
2260−
2261−instance R.MonadReflexHost (SpiderTimeline x) (SpiderHost x) where
2262− type ReadPhase (SpiderHost x) = ReadPhase x
2263− fireEventsAndRead es (ReadPhase a) = run es a
2264− runHostFrame = runFrame . runSpiderHostFrame
2265−
22662009instance MonadRef (SpiderHost x) where
22672010 type Ref (SpiderHost x) = Ref IO
22682011 newRef = SpiderHost . newRef
@@ -2281,9 +2024,6 @@ instance MonadRef (SpiderHostFrame x) where
22812024instance MonadAtomicRef (SpiderHostFrame x) where
22822025 atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
22832026
2284−--------------------------------------------------------------------------------
2285−-- Deprecated items
2286−--------------------------------------------------------------------------------
2287−
2288−{-# DEPRECATED SpiderEnv "Use 'SpiderTimeline' instead" #-}
2289−type SpiderEnv = SpiderTimeline
2027+instance PrimMonad (SpiderHostFrame x) where
2028+ type PrimState (SpiderHostFrame x) = PrimState IO
2029+ primitive = SpiderHostFrame . EventM . primitive
src/Reflex/Spider/SpiderTimeline.include.hsnew file+121
@@ -0,0 +1,121 @@
1+-- | Designates the default, global Spider timeline
2+data SpiderTimeline x
3+type role SpiderTimeline nominal
4+
5+-- | The default, global Spider environment
6+type Spider = SpiderTimeline Global
7+
8+instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (EventM x) where
9+ {-# INLINABLE sample #-}
10+ sample (SpiderBehavior b) = readBehaviorUntracked b
11+
12+instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (EventM x) where
13+ {-# INLINABLE hold #-}
14+ hold = holdSpiderEventM
15+ {-# INLINABLE holdDyn #-}
16+ holdDyn = holdDynSpiderEventM
17+ {-# INLINABLE holdIncremental #-}
18+ holdIncremental = holdIncrementalSpiderEventM
19+
20+instance (
21+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
22+ x ~ Global
23+#endif
24+ ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
25+ {-# INLINABLE sample #-}
26+ sample = coerce . readBehaviorTracked . unSpiderBehavior
27+
28+instance (
29+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
30+ x ~ Global
31+#endif
32+ ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPushM x) where
33+ {-# INLINABLE sample #-}
34+ sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
35+
36+instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderPushM x) where
37+ {-# INLINABLE hold #-}
38+ hold v0 e = Reflex.Class.current <$> Reflex.Class.holdDyn v0 e
39+ {-# INLINABLE holdDyn #-}
40+ holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce e
41+ {-# INLINABLE holdIncremental #-}
42+ holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . dynamicHold <$> Reflex.Spider.Internal.hold v0 e
43+
44+instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
45+ {-# INLINE return #-}
46+ return = pure
47+ {-# INLINE (>>=) #-}
48+ x >>= f = SpiderDynamic $ dynamicDynIdentity $ newJoinDyn $ newMapDyn (unSpiderDynamic . f) $ unSpiderDynamic x
49+ {-# INLINE (>>) #-}
50+ _ >> y = y
51+ {-# INLINE fail #-}
52+ fail _ = error "Dynamic does not support 'fail'"
53+
54+newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
55+newJoinDyn d =
56+ let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
57+ eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
58+ eInner = Reflex.Spider.Internal.switch $ dynamicUpdated <$> dynamicCurrent d
59+ eBoth = Reflex.Spider.Internal.coincidence $ dynamicUpdated . runIdentity <$> dynamicUpdated d
60+ v' = unSpiderEvent $ Reflex.Class.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
61+ in unsafeDyn readV0 v'
62+
63+instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
64+ fmap f = SpiderDynamic . newMapDyn f . unSpiderDynamic
65+
66+instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
67+ pure = SpiderDynamic . dynamicConst
68+ SpiderDynamic a <*> SpiderDynamic b = SpiderDynamic $ Reflex.Spider.Internal.zipDynWith ($) a b
69+ _ *> b = b
70+ a <* _ = a
71+
72+holdSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Behavior (SpiderTimeline x) a)
73+holdSpiderEventM v0 e = fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
74+
75+holdDynSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Dynamic (SpiderTimeline x) a)
76+holdDynSpiderEventM v0 e = fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
77+
78+holdIncrementalSpiderEventM :: (HasSpiderTimeline x, Patch p) => PatchTarget p -> Reflex.Class.Event (SpiderTimeline x) p -> EventM x (Reflex.Class.Incremental (SpiderTimeline x) p)
79+holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
80+
81+instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHost x) where
82+ {-# INLINABLE hold #-}
83+ hold v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.hold v0 e
84+ {-# INLINABLE holdDyn #-}
85+ holdDyn v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdDyn v0 e
86+ {-# INLINABLE holdIncremental #-}
87+ holdIncremental v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdIncremental v0 e
88+
89+instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
90+ sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
91+
92+instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHostFrame x) where
93+ {-# INLINABLE hold #-}
94+ hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
95+ {-# INLINABLE holdDyn #-}
96+ holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
97+ {-# INLINABLE holdIncremental #-}
98+ holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
99+
100+instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
101+ {-# INLINABLE sample #-}
102+ sample = runFrame . readBehaviorUntracked . unSpiderBehavior
103+
104+instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
105+ {-# INLINABLE sample #-}
106+ sample = Reflex.Spider.Internal.ReadPhase . Reflex.Class.sample
107+
108+instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
109+ {-# INLINABLE hold #-}
110+ hold v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.hold v0 e
111+ {-# INLINABLE holdDyn #-}
112+ holdDyn v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdDyn v0 e
113+ {-# INLINABLE holdIncremental #-}
114+ holdIncremental v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdIncremental v0 e
115+
116+--------------------------------------------------------------------------------
117+-- Deprecated items
118+--------------------------------------------------------------------------------
119+
120+{-# DEPRECATED SpiderEnv "Use 'SpiderTimelineEnv' instead" #-}
121+type SpiderEnv = SpiderTimeline
src/Reflex/Spider/SpiderTimelineHost.include.hsnew file+57
@@ -0,0 +1,57 @@
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
test/Reflex/Test/Micro.hs+21−4
@@ -1,9 +1,11 @@
1+{-# LANGUAGE BangPatterns #-}
12{-# LANGUAGE ConstraintKinds #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE FlexibleInstances #-}
45{-# LANGUAGE GADTs #-}
56{-# LANGUAGE MultiParamTypeClasses #-}
67{-# LANGUAGE RankNTypes #-}
8+{-# LANGUAGE RecursiveDo #-}
79
810module Reflex.Test.Micro (testCases) where
911
@@ -12,16 +14,16 @@ import Reflex.TestPlan
1214
1315import Control.Applicative
1416import Control.Monad
17+import Control.Monad.Fix
1518import Data.Char
16−import Data.Functor.Misc
17−import Data.Monoid
18−
1919import Data.Foldable
20−
20+import Data.Functor.Misc
2121import qualified Data.Map as Map
22+import Data.Monoid
2223
2324import Prelude
2425
26+{-# ANN testCases "HLint: ignore Functor law" #-}
2527testCases :: [(String, TestCase)]
2628testCases =
2729 [ testB "hold" $ hold "0" =<< events1
@@ -50,6 +52,13 @@ testCases =
5052 pushAlways (const $ hold "z" es) e
5153 return (join b)
5254
55+ , testE "id" $ do
56+ events2
57+
58+ , testE "fmap-id" $ do
59+ e <- events2
60+ return $ fmap id e
61+
5362 , testE "tag-1" $ do
5463 b1 <- behavior1
5564 e <- events2
@@ -247,6 +256,14 @@ testCases =
247256 e2 <- events2
248257 return $ e1 `difference ` e2
249258
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+
250267 ] where
251268
252269 events1, events2, events3 :: TestPlan t m => m (Event t String)