on develop help sign in
Merge remote-tracking branch 'origin/wip-optimizer' into develop
3835a1fRyan Trinkle
.gitignore+3
@@ -1,6 +1,7 @@
1dist
1dist
2cabal.sandbox.config
2cabal.sandbox.config
3.cabal-sandbox/
3.cabal-sandbox/
4+dist-*
4cabal-dev
5cabal-dev
5*.o
6*.o
6*.hi
7*.hi
@@ -33,3 +34,5 @@ hsenv.log
33.#*
34.#*
34/shell.nix
35/shell.nix
35/ghci-tmp
36/ghci-tmp
37+*.dump-*
38+*.verbose-core2core
bench/RunAll.hs+1−1
@@ -74,7 +74,7 @@ newtype Ignore a = Ignore a
74instance NFData (Ignore a) where
74instance NFData (Ignore a) where
75 rnf !_ = ()
75 rnf !_ = ()
76
76
77−instance NFData (SpiderEventHandle a) where
77+instance NFData (SpiderEventHandle x a) where
78 rnf !_ = ()
78 rnf !_ = ()
79
79
80instance NFData (Behavior t a) where
80instance NFData (Behavior t a) where
default.nix+13−13
@@ -1,24 +1,24 @@
1−{ mkDerivation, base, bifunctors, containers, deepseq
1+{ mkDerivation, base, bifunctors, containers, data-default, deepseq
2−, dependent-map, dependent-sum, exception-transformers
2+, dependent-map, dependent-sum, exception-transformers, ghc
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
3+, haskell-src-exts, haskell-src-meta, lens, MemoTrie, monad-control
4−, monad-control, mtl , primitive, ref-tf, semigroupoids
4+, mtl, prim-uniq, primitive, ref-tf, semigroupoids, semigroups
5−, semigroups, split, stdenv, stm , syb, template-haskell
5+, split, stdenv, stm, syb, template-haskell, these, transformers
6−, these, transformers, transformers-compat
6+, transformers-compat
7}:
7}:
8mkDerivation {
8mkDerivation {
9 pname = "reflex";
9 pname = "reflex";
10− version = "0.4.0";
10+ version = "0.5.0";
11 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
11 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
12 libraryHaskellDepends = [
12 libraryHaskellDepends = [
13− base bifunctors containers dependent-map dependent-sum
13+ base bifunctors containers data-default dependent-map dependent-sum
14− exception-transformers haskell-src-exts haskell-src-meta lens
14+ exception-transformers ghc haskell-src-exts haskell-src-meta lens
15− MemoTrie monad-control mtl primitive ref-tf semigroupoids
15+ MemoTrie monad-control mtl prim-uniq primitive ref-tf semigroupoids
16− semigroups stm syb template-haskell these transformers
16+ semigroups stm syb template-haskell these transformers
17 transformers-compat
17 transformers-compat
18 ];
18 ];
19 testHaskellDepends = [
19 testHaskellDepends = [
20− base bifunctors containers deepseq dependent-map dependent-sum
20+ base bifunctors containers deepseq dependent-map dependent-sum mtl
21− hlint mtl ref-tf split transformers
21+ ref-tf split transformers
22 ];
22 ];
23 homepage = "https://github.com/reflex-frp/reflex";
23 homepage = "https://github.com/reflex-frp/reflex";
24 description = "Higher-order Functional Reactive Programming";
24 description = "Higher-order Functional Reactive Programming";
reflex.cabal+25−10
@@ -13,6 +13,11 @@ Cabal-version: >=1.9.2
13homepage: https://github.com/reflex-frp/reflex
13homepage: https://github.com/reflex-frp/reflex
14bug-reports: https://github.com/reflex-frp/reflex/issues
14bug-reports: https://github.com/reflex-frp/reflex/issues
15
15
16+flag specialize-to-spidertimeline-global
17+ description: Specialize all Reflex functions to the SpiderTimeline Global implementation. This may improve performance, but will disable all other implementations.
18+ default: False
19+ manual: True
20+
16library
21library
17 hs-source-dirs: src
22 hs-source-dirs: src
18 build-depends:
23 build-depends:
@@ -24,11 +29,13 @@ library
24 dependent-map >= 0.2.2 && < 0.3,
29 dependent-map >= 0.2.2 && < 0.3,
25 dependent-sum == 0.3.*,
30 dependent-sum == 0.3.*,
26 exception-transformers == 0.4.*,
31 exception-transformers == 0.4.*,
32+ ghc,
27 haskell-src-exts >= 1.16 && < 1.18,
33 haskell-src-exts >= 1.16 && < 1.18,
28 haskell-src-meta == 0.6.*,
34 haskell-src-meta == 0.6.*,
29 lens >= 4.7 && < 5,
35 lens >= 4.7 && < 5,
30 monad-control >= 1.0.1 && < 1.1,
36 monad-control >= 1.0.1 && < 1.1,
31 mtl >= 2.1 && < 2.3,
37 mtl >= 2.1 && < 2.3,
38+ prim-uniq >= 0.1.0.1 && < 0.2,
32 primitive >= 0.5 && < 0.7,
39 primitive >= 0.5 && < 0.7,
33 ref-tf == 0.4.*,
40 ref-tf == 0.4.*,
34 semigroupoids >= 4.0 && < 6,
41 semigroupoids >= 4.0 && < 6,
@@ -45,16 +52,17 @@ library
45 Data.WeakBag,
52 Data.WeakBag,
46 Reflex,
53 Reflex,
47 Reflex.Class,
54 Reflex.Class,
48− Reflex.Deletable.Class,
49 Reflex.Dynamic,
55 Reflex.Dynamic,
50 Reflex.Dynamic.TH,
56 Reflex.Dynamic.TH,
51 Reflex.Dynamic.Uniq,
57 Reflex.Dynamic.Uniq,
52 Reflex.DynamicWriter,
58 Reflex.DynamicWriter,
59+ Reflex.FunctorMaybe,
53 Reflex.Host.Class,
60 Reflex.Host.Class,
61+ Reflex.Optimizer,
62+ Reflex.Patch,
54 Reflex.PerformEvent.Base,
63 Reflex.PerformEvent.Base,
55 Reflex.PerformEvent.Class,
64 Reflex.PerformEvent.Class,
56 Reflex.PostBuild.Class,
65 Reflex.PostBuild.Class,
57− Reflex.Pure,
58 Reflex.Spider,
66 Reflex.Spider,
59 Reflex.Spider.Internal
67 Reflex.Spider.Internal
60
68
@@ -62,6 +70,11 @@ library
62 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
70 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
63 ghc-prof-options: -auto-all
71 ghc-prof-options: -auto-all
64
72
73+ if flag(specialize-to-spidertimeline-global)
74+ cpp-options: -DSPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
75+ else
76+ exposed-modules: Reflex.Pure
77+
65test-suite semantics
78test-suite semantics
66 type: exitcode-stdio-1.0
79 type: exitcode-stdio-1.0
67 main-is: semantics.hs
80 main-is: semantics.hs
@@ -79,15 +92,17 @@ test-suite semantics
79 reflex,
92 reflex,
80 split,
93 split,
81 transformers >= 0.3
94 transformers >= 0.3
82−
95+ if flag(specialize-to-spidertimeline-global)
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)
89 buildable: False
96 buildable: False
90
97
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+
91benchmark spider-bench
106benchmark spider-bench
92 type: exitcode-stdio-1.0
107 type: exitcode-stdio-1.0
93 hs-source-dirs: bench
108 hs-source-dirs: bench
@@ -123,7 +138,7 @@ benchmark saulzar-bench
123 mtl,
138 mtl,
124 primitive,
139 primitive,
125 process,
140 process,
126− ref-tf == 0.4,
141+ ref-tf,
127 reflex,
142 reflex,
128 split,
143 split,
129 stm,
144 stm,
src/Data/Functor/Misc.hs+56
@@ -3,11 +3,13 @@
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GADTs #-}
4{-# LANGUAGE GADTs #-}
5{-# LANGUAGE KindSignatures #-}
5{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE PolyKinds #-}
8{-# LANGUAGE PolyKinds #-}
8{-# LANGUAGE RankNTypes #-}
9{-# LANGUAGE RankNTypes #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE StandaloneDeriving #-}
11{-# LANGUAGE StandaloneDeriving #-}
12+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
11-- | This module provides types and functions with no particular theme, but
13-- | This module provides types and functions with no particular theme, but
12-- which are relevant to the use of 'Functor'-based datastructures like
14-- which are relevant to the use of 'Functor'-based datastructures like
13-- 'Data.Dependent.Map.DMap'.
15-- 'Data.Dependent.Map.DMap'.
@@ -20,7 +22,12 @@ module Data.Functor.Misc
20 , WrapArg (..)
22 , WrapArg (..)
21 -- * Convenience functions for DMap
23 -- * Convenience functions for DMap
22 , mapWithFunctorToDMap
24 , mapWithFunctorToDMap
25+ , mapKeyValuePairsMonotonic
23 , combineDMapsWithKey
26 , combineDMapsWithKey
27+ , EitherTag (..)
28+ , dmapToThese
29+ , eitherToDSum
30+ , dsumToEither
24 -- * Deprecated functions
31 -- * Deprecated functions
25 , sequenceDmap
32 , sequenceDmap
26 , wrapDMap
33 , wrapDMap
@@ -132,6 +139,55 @@ combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList m
132 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
139 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
133 GGT -> (hk :=> f hk (That hv)) : go gs hs'
140 GGT -> (hk :=> f hk (That hv)) : go gs hs'
134
141
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+
135--------------------------------------------------------------------------------
191--------------------------------------------------------------------------------
136-- Deprecated functions
192-- Deprecated functions
137--------------------------------------------------------------------------------
193--------------------------------------------------------------------------------
src/Data/WeakBag.hs+22−24
@@ -1,3 +1,6 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE ExistentialQuantification #-}
3+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1-- | This module defines the 'WeakBag' type, which represents a mutable
4-- | This module defines the 'WeakBag' type, which represents a mutable
2-- collection of items that does not cause the items to be retained in memory.
5-- collection of items that does not cause the items to be retained in memory.
3-- This is useful for situations where a value needs to be inspected or modified
6-- This is useful for situations where a value needs to be inspected or modified
@@ -12,7 +15,6 @@ module Data.WeakBag
12 , remove
15 , remove
13 ) where
16 ) where
14
17
15−import Control.Concurrent.STM
16import Control.Exception
18import Control.Exception
17import Control.Monad hiding (forM_, mapM_)
19import Control.Monad hiding (forM_, mapM_)
18import Control.Monad.IO.Class
20import Control.Monad.IO.Class
@@ -28,15 +30,15 @@ import Prelude hiding (mapM_, traverse)
28-- that is, they can still be garbage-collected. As long as the @a@s remain
30-- that is, they can still be garbage-collected. As long as the @a@s remain
29-- alive, the 'WeakBag' will continue to refer to them.
31-- alive, the 'WeakBag' will continue to refer to them.
30data WeakBag a = WeakBag
32data WeakBag a = WeakBag
31− { _weakBag_nextId :: {-# UNPACK #-} !(TVar Int) --TODO: what if this wraps around?
33+ { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
32− , _weakBag_children :: {-# UNPACK #-} !(TVar (IntMap (Weak a)))
34+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
33 }
35 }
34
36
35-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
37-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
36-- the caller retains the ticket, the item is guranteed to stay in memory (and
38-- the caller retains the ticket, the item is guranteed to stay in memory (and
37-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
39-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
38-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
40-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
39−data WeakBagTicket a = WeakBagTicket
41+data WeakBagTicket = forall a. WeakBagTicket
40 { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
42 { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
41 , _weakBagTicket_item :: {-# NOUNPACK #-} !a
43 , _weakBagTicket_item :: {-# NOUNPACK #-} !a
42 }
44 }
@@ -50,30 +52,24 @@ insert :: a -- ^ The item
50 -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
52 -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
51 -- (whether automatically by the item being garbage
53 -- (whether automatically by the item being garbage
52 -- collected or manually via 'remove')
54 -- collected or manually via 'remove')
53− -> IO (WeakBagTicket a) -- ^ Returns a 'WeakBagTicket' that ensures the
55+ -> IO WeakBagTicket -- ^ Returns a 'WeakBagTicket' that ensures the item
54− -- item is retained and allows the item to be
56+ -- is retained and allows the item to be removed.
55− -- removed.
56insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
57insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
57 a' <- evaluate a
58 a' <- evaluate a
58 wbRef' <- evaluate wbRef
59 wbRef' <- evaluate wbRef
59− myId <- atomically $ do
60+ myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
60− myId <- readTVar nextId
61− writeTVar nextId $! succ myId
62− return myId
63 let cleanup = do
61 let cleanup = do
64 wb <- readIORef wbRef'
62 wb <- readIORef wbRef'
65 mb <- deRefWeak wb
63 mb <- deRefWeak wb
66 forM_ mb $ \b -> do
64 forM_ mb $ \b -> do
67− isLastNode <- atomically $ do --TODO: Should this run even when mb is Nothing?
65+ csWithoutMe <- atomicModifyIORef children $ \cs ->
68− cs <- readTVar children
69− let csWithoutMe = IntMap.delete myId cs
66+ let !csWithoutMe = IntMap.delete myId cs
70− writeTVar children $! csWithoutMe
67+ in (csWithoutMe, csWithoutMe)
71− return $ IntMap.size csWithoutMe == 0
68+ when (IntMap.null csWithoutMe) $ finalizer b
72− when isLastNode $ finalizer b
73 return ()
69 return ()
74 return ()
70 return ()
75 wa <- mkWeakPtr a' $ Just cleanup
71 wa <- mkWeakPtr a' $ Just cleanup
76− atomically $ modifyTVar' children $ IntMap.insert myId wa
72+ atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
77 return $ WeakBagTicket
73 return $ WeakBagTicket
78 { _weakBagTicket_weakItem = wa
74 { _weakBagTicket_weakItem = wa
79 , _weakBagTicket_item = a'
75 , _weakBagTicket_item = a'
@@ -83,8 +79,8 @@ insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
83{-# INLINE empty #-}
79{-# INLINE empty #-}
84empty :: IO (WeakBag a)
80empty :: IO (WeakBag a)
85empty = {-# SCC "empty" #-} do
81empty = {-# SCC "empty" #-} do
86− nextId <- newTVarIO 1
82+ nextId <- newIORef 1
87− children <- newTVarIO IntMap.empty
83+ children <- newIORef IntMap.empty
88 let bag = WeakBag
84 let bag = WeakBag
89 { _weakBag_nextId = nextId
85 { _weakBag_nextId = nextId
90 , _weakBag_children = children
86 , _weakBag_children = children
@@ -94,7 +90,7 @@ empty = {-# SCC "empty" #-} do
94-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
90-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
95-- 'empty', then using 'insert'.
91-- 'empty', then using 'insert'.
96{-# INLINE singleton #-}
92{-# 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)
98singleton a wbRef finalizer = {-# SCC "singleton" #-} do
94singleton a wbRef finalizer = {-# SCC "singleton" #-} do
99 bag <- empty
95 bag <- empty
100 ticket <- insert a bag wbRef finalizer
96 ticket <- insert a bag wbRef finalizer
@@ -107,7 +103,7 @@ singleton a wbRef finalizer = {-# SCC "singleton" #-} do
107-- is made about the order of the traversal.
103-- is made about the order of the traversal.
108traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
104traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
109traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
105traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
110− cs <- liftIO $ readTVarIO children
106+ cs <- liftIO $ readIORef children
111 forM_ cs $ \c -> do
107 forM_ cs $ \c -> do
112 ma <- liftIO $ deRefWeak c
108 ma <- liftIO $ deRefWeak c
113 mapM_ f ma
109 mapM_ f ma
@@ -115,6 +111,8 @@ traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
115-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
111-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
116-- on the same 'WeakBagTicket'.
112-- on the same 'WeakBagTicket'.
117{-# INLINE remove #-}
113{-# INLINE remove #-}
118−remove :: WeakBagTicket a -> IO ()
114+remove :: WeakBagTicket -> IO ()
119−remove = {-# SCC "remove" #-} finalize . _weakBagTicket_weakItem
115+remove (WeakBagTicket w _) = {-# SCC "remove" #-} finalize w
120--TODO: Should 'remove' also drop the reference to the item?
116--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 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveFunctor #-}
3+{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
@@ -8,24 +10,32 @@
8{-# LANGUAGE NoMonomorphismRestriction #-}
10{-# LANGUAGE NoMonomorphismRestriction #-}
9{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE RecursiveDo #-}
12{-# LANGUAGE RecursiveDo #-}
13+{-# LANGUAGE RoleAnnotations #-}
11{-# LANGUAGE ScopedTypeVariables #-}
14{-# LANGUAGE ScopedTypeVariables #-}
15+{-# LANGUAGE StandaloneDeriving #-}
12{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeOperators #-}
17{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE UndecidableInstances #-}
18{-# LANGUAGE UndecidableInstances #-}
19+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
21+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
22+#endif
15
23
16-- | This module contains the Reflex interface, as well as a variety of
24-- | This module contains the Reflex interface, as well as a variety of
17-- convenience functions for working with 'Event's, 'Behavior's, and other
25-- convenience functions for working with 'Event's, 'Behavior's, and other
18-- signals.
26-- signals.
19module Reflex.Class
27module Reflex.Class
28+ ( module Reflex.Patch
20− ( -- * Primitives
29+ -- * Primitives
21− Reflex (..)
30+ , Reflex (..)
31+ , coerceBehavior
32+ , coerceEvent
33+ , coerceDynamic
22 , MonadSample (..)
34 , MonadSample (..)
23 , MonadHold (..)
35 , MonadHold (..)
24 -- ** 'fan'-related types
36 -- ** 'fan'-related types
25 , EventSelector (..)
37 , EventSelector (..)
26 -- ** 'Incremental'-related types
38 -- ** 'Incremental'-related types
27− , Patch (..)
28− , PatchDMap (..)
29 -- * Convenience functions
39 -- * Convenience functions
30 , constDyn
40 , constDyn
31 , pushAlways
41 , pushAlways
@@ -81,10 +91,40 @@ module Reflex.Class
81 , ffilter
91 , ffilter
82 -- * Miscellaneous convenience functions
92 -- * Miscellaneous convenience functions
83 , ffor
93 , ffor
94+ , MonadAdjust (..)
84 -- * Deprecated functions
95 -- * Deprecated functions
85 , appendEvents
96 , appendEvents
86 , onceE
97 , onceE
87 , sequenceThese
98 , 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
88 ) where
128 ) where
89
129
90import Control.Applicative
130import Control.Applicative
@@ -97,34 +137,44 @@ import Control.Monad.Trans.RWS (RWST ())
97import Control.Monad.Trans.Writer (WriterT ())
137import Control.Monad.Trans.Writer (WriterT ())
98import Data.Align
138import Data.Align
99import Data.Bifunctor
139import Data.Bifunctor
140+import Data.Coerce
100import Data.Default
141import Data.Default
101−import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
142+import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
102import qualified Data.Dependent.Map as DMap
143import qualified Data.Dependent.Map as DMap
103−import Data.Dependent.Sum (ShowTag (..))
104import Data.Either
144import Data.Either
105import Data.Foldable
145import Data.Foldable
106import Data.Functor.Bind hiding (join)
146import Data.Functor.Bind hiding (join)
107import qualified Data.Functor.Bind as Bind
147import qualified Data.Functor.Bind as Bind
108−import Data.Functor.Compose
109−import Data.Functor.Constant
110import Data.Functor.Misc
148import Data.Functor.Misc
111import Data.Functor.Plus
149import Data.Functor.Plus
112−import Data.GADT.Compare ((:~:) (..), GEq (..))
113−import Data.GADT.Show (GShow (..))
114import Data.List.NonEmpty (NonEmpty (..))
150import Data.List.NonEmpty (NonEmpty (..))
115import Data.Map (Map)
151import Data.Map (Map)
116import Data.Maybe
152import Data.Maybe
117import Data.Monoid hiding (Alt, (<>))
153import Data.Monoid hiding (Alt, (<>))
118−import Data.Semigroup (Semigroup, sconcat, stimes, stimesIdempotentMonoid, (<>))
154+import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
119import Data.String
155import Data.String
120import Data.These
156import Data.These
121import Data.Traversable
157import 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
122
168
123-- Note: must come last to silence warnings due to AMP on GHC < 7.10
169-- Note: must come last to silence warnings due to AMP on GHC < 7.10
124import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
170import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
125
171
126import Debug.Trace (trace)
172import Debug.Trace (trace)
127
173
174+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
175+#include "Spider/SpiderTimeline.include.hs"
176+#endif
177+
128-- | The 'Reflex' class contains all the primitive functionality needed for
178-- | The 'Reflex' class contains all the primitive functionality needed for
129-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
179-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
130-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
180-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
@@ -133,11 +183,12 @@ import Debug.Trace (trace)
133class ( MonadHold t (PushM t)
183class ( MonadHold t (PushM t)
134 , MonadSample t (PullM t)
184 , MonadSample t (PullM t)
135 , MonadFix (PushM t)
185 , MonadFix (PushM t)
136− , Functor (Event t)
137− , Functor (Behavior t)
138 , Functor (Dynamic t)
186 , Functor (Dynamic t)
139 , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
187 , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
140 , Monad (Dynamic t)
188 , Monad (Dynamic t)
189+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
190+ , t ~ SpiderTimeline Global
191+#endif
141 ) => Reflex t where
192 ) => Reflex t where
142 -- | A container for a value that can change over time. 'Behavior's can be
193 -- | A container for a value that can change over time. 'Behavior's can be
143 -- sampled at will, but it is not possible to be notified when they change
194 -- sampled at will, but it is not possible to be notified when they change
@@ -155,7 +206,12 @@ class ( MonadHold t (PushM t)
155 -- notifications on changes. Basically a combination of a 'Behavior' and an
206 -- notifications on changes. Basically a combination of a 'Behavior' and an
156 -- 'Event', with a rule that the 'Behavior' will change if and only if the
207 -- 'Event', with a rule that the 'Behavior' will change if and only if the
157 -- 'Event' fires.
208 -- '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
159 -- | An 'Event' with no occurrences
215 -- | An 'Event' with no occurrences
160 never :: Event t a
216 never :: Event t a
161 -- | Create a 'Behavior' that always has the given value
217 -- | Create a 'Behavior' that always has the given value
@@ -164,13 +220,9 @@ class ( MonadHold t (PushM t)
164 -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
220 -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
165 -- (Just) or non-occurring (Nothing) result
221 -- (Just) or non-occurring (Nothing) result
166 push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
222 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 :: * -> *
169 -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
223 -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
170 -- recomputed whenever any of the read 'Behavior's changes
224 -- recomputed whenever any of the read 'Behavior's changes
171 pull :: PullM t a -> Behavior t a
225 pull :: PullM t a -> Behavior t a
172− -- | A monad for doing complex pull-based calculations efficiently
173− type PullM t :: * -> *
174 -- | Merge a collection of events; the resulting 'Event' will only occur if at
226 -- | Merge a collection of events; the resulting 'Event' will only occur if at
175 -- least one input event is occuring, and will contain all of the input keys
227 -- least one input event is occuring, and will contain all of the input keys
176 -- that are occurring simultaneously
228 -- that are occurring simultaneously
@@ -195,52 +247,103 @@ class ( MonadHold t (PushM t)
195 -- | Create a new 'Incremental'. The given 'PullM''s value must always change
247 -- | Create a new 'Incremental'. The given 'PullM''s value must always change
196 -- in the same way that the accumulated application of patches would change
248 -- in the same way that the accumulated application of patches would change
197 -- that value.
249 -- 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
199 -- | Create a merge whose parents can change over time
251 -- | 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)
201 -- | Extract the 'Behavior' component of an 'Incremental'
253 -- | 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)
203 -- | Extract the 'Event' component of an 'Incremental'
255 -- | 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
205 -- | Convert an 'Incremental' to a 'Dynamic'
257 -- | Convert an 'Incremental' to a 'Dynamic'
206− incrementalToDynamic :: Patch p => Incremental t p a -> Dynamic t a
258+ incrementalToDynamic :: Patch p => Incremental t p -> Dynamic t (PatchTarget p)
207−
259+ -- | Construct a 'Coercion' for a 'Behavior' given an 'Coercion' for its
208−-- | A 'Patch' type represents a kind of change made to a datastructure.
260+ -- occurrence type
209−class Patch p where
261+ behaviorCoercion :: Coercion a b -> Coercion (Behavior t a) (Behavior t b)
210− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
262+ -- | Construct a 'Coercion' for an 'Event' given an 'Coercion' for its
211− -- 'Nothing'.
263+ -- occurrence type
212− apply :: p a -> a -> Maybe a
264+ eventCoercion :: Coercion a b -> Coercion (Event t a) (Event t b)
213−
265+ -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
214−instance Patch Identity where
266+ -- occurrence type
215− apply (Identity a) _ = Just a
267+ dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
216−
268+#endif
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)
220
269
221−instance GCompare k => Semigroup (PatchDMap (DMap k v)) where
270+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
222− PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
271+instance t ~ SpiderTimeline Global => Reflex t where
223− -- PatchDMap is idempotent, so stimes n is id for every n
272+ newtype Behavior t a = SpiderBehavior { unSpiderBehavior :: Reflex.Spider.Internal.Behavior Global a }
224−#if MIN_VERSION_semigroups(0,17,0)
273+ newtype Event t a = SpiderEvent { unSpiderEvent :: Reflex.Spider.Internal.Event Global a }
225− stimes = stimesIdempotentMonoid
274+ newtype Dynamic t a = SpiderDynamic { unSpiderDynamic :: Reflex.Spider.Internal.Dynamic Global (Identity a) } -- deriving (Functor, Applicative, Monad)
226−#else
275+ newtype Incremental t p = SpiderIncremental { unSpiderIncremental :: Reflex.Spider.Internal.Dynamic Global p }
227− times1p n x = case compare n 0 of
276+ type PullM t = SpiderPullM Global
228− LT -> error "stimesIdempotentMonoid: negative multiplier"
277+ type PushM t = SpiderPushM Global
229− EQ -> mempty
278+never :: Reflex t => Event t a
230− GT -> x
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
231#endif
334#endif
232
335
233−instance GCompare k => Monoid (PatchDMap (DMap k v)) where
336+-- | Coerce a 'Behavior' between representationally-equivalent value types
234− mempty = PatchDMap mempty
337+coerceBehavior :: (Reflex t, Coercible a b) => Behavior t a -> Behavior t b
235− mappend = (<>)
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
236
343
237−instance Patch PatchDMap where
344+-- | Coerce a 'Dynamic' between representationally-equivalent value types
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?
345+coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
239− where insertions = DMap.mapMaybeWithKey (const $ getCompose) diff
346+coerceDynamic = coerceWith $ dynamicCoercion Coercion
240− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getCompose) diff
241− nothingToJust = \case
242− Nothing -> Just $ Constant ()
243− Just _ -> Nothing
244
347
245-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
348-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
246-- _must_ change when and only when the 'Event' fires, such that the
349-- _must_ change when and only when the 'Event' fires, such that the
@@ -279,7 +382,7 @@ class MonadSample t m => MonadHold t m where
279 holdDyn :: a -> Event t a -> m (Dynamic t a)
382 holdDyn :: a -> Event t a -> m (Dynamic t a)
280 -- | Create an 'Incremental' value using the given initial value that changes
383 -- | Create an 'Incremental' value using the given initial value that changes
281 -- every time the 'Event' occurs.
384 -- 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)
283
386
284-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
387-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
285-- key. This is much more efficient than filtering for each key with
388-- 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
412 times1p n = fmap $ times1p n
515 times1p n = fmap $ times1p n
413#endif
516#endif
414
517
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−
437-- | Flipped version of 'fmapMaybe'.
518-- | Flipped version of 'fmapMaybe'.
438fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
519fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
439fforMaybe = flip fmapMaybe
520fforMaybe = flip fmapMaybe
@@ -458,9 +539,11 @@ instance Reflex t => Bind (Event t) where
458 join = coincidence
539 join = coincidence
459
540
460instance Reflex t => Functor (Event t) where
541instance Reflex t => Functor (Event t) where
542+ {-# INLINE fmap #-}
461 fmap f = fmapMaybe $ Just . f
543 fmap f = fmapMaybe $ Just . f
462
544
463instance Reflex t => FunctorMaybe (Event t) where
545instance Reflex t => FunctorMaybe (Event t) where
546+ {-# INLINE fmapMaybe #-}
464 fmapMaybe f = push $ return . f
547 fmapMaybe f = push $ return . f
465
548
466-- | Never: @'zero' = 'never'@.
549-- | Never: @'zero' = 'never'@.
@@ -494,7 +577,6 @@ headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
494headE e = do
577headE e = do
495 rec be <- hold e $ fmap (const never) e'
578 rec be <- hold e $ fmap (const never) e'
496 let e' = switch be
579 let e' = switch be
497− e' `seq` return ()
498 return e'
580 return e'
499
581
500-- | Create a new 'Event' that occurs on all but the first occurence of the
582-- | 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
532traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
614traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
533traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
615traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
534
616
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−
583instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
617instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
584 (<>) = alignWith (mergeThese (<>))
618 (<>) = alignWith (mergeThese (<>))
585 sconcat = fmap sconcat . mergeList . toList
619 sconcat = fmap sconcat . mergeList . toList
@@ -597,6 +631,7 @@ instance (Semigroup a, Reflex t) => Monoid (Event t a) where
597-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
631-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
598-- list occurs. If multiple occur at the same time they are folded from the left
632-- list occurs. If multiple occur at the same time they are folded from the left
599-- with the given function.
633-- with the given function.
634+{-# INLINE mergeWith #-}
600mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
635mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
601mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
636mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
602 . merge
637 . merge
@@ -607,6 +642,7 @@ mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v)
607-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
642-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
608-- list occurs. If multiple occur at the same time the value is the value of the
643-- list occurs. If multiple occur at the same time the value is the value of the
609-- leftmost event.
644-- leftmost event.
645+{-# INLINE leftmost #-}
610leftmost :: Reflex t => [Event t a] -> Event t a
646leftmost :: Reflex t => [Event t a] -> Event t a
611leftmost = mergeWith const
647leftmost = mergeWith const
612
648
@@ -762,21 +798,21 @@ class Reflex t => Accumulator t f | f -> t where
762
798
763-- | Accumulate occurrences of an 'Event', producing an output occurrence each
799-- | Accumulate occurrences of an 'Event', producing an output occurrence each
764-- time. Discard the underlying 'Accumulator'.
800-- 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)
766mapAccum_ f z e = do
802mapAccum_ f z e = do
767 (_ :: Behavior t a, result) <- mapAccum f z e
803 (_ :: Behavior t a, result) <- mapAccum f z e
768 return result
804 return result
769
805
770-- | Accumulate occurrences of an 'Event', possibly producing an output
806-- | Accumulate occurrences of an 'Event', possibly producing an output
771-- occurrence each time. Discard the underlying 'Accumulator'.
807-- 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)
773mapAccumMaybe_ f z e = do
809mapAccumMaybe_ f z e = do
774 (_ :: Behavior t a, result) <- mapAccumMaybe f z e
810 (_ :: Behavior t a, result) <- mapAccumMaybe f z e
775 return result
811 return result
776
812
777-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
813-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
778-- an output occurrence each time. Discard the underlying 'Accumulator'.
814-- 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)
780mapAccumM_ f z e = do
816mapAccumM_ f z e = do
781 (_ :: Behavior t a, result) <- mapAccumM f z e
817 (_ :: Behavior t a, result) <- mapAccumM f z e
782 return result
818 return result
@@ -784,7 +820,7 @@ mapAccumM_ f z e = do
784-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
820-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
785-- producing an output occurrence each time. Discard the underlying
821-- producing an output occurrence each time. Discard the underlying
786-- 'Accumulator'.
822-- '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)
788mapAccumMaybeM_ f z e = do
824mapAccumMaybeM_ f z e = do
789 (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
825 (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
790 return result
826 return result
@@ -824,6 +860,21 @@ zipListWithEvent f l e = do
824 _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
860 _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
825 mapAccumMaybe_ f' l e
861 mapAccumMaybe_ f' l e
826
862
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+
827--------------------------------------------------------------------------------
878--------------------------------------------------------------------------------
828-- Deprecated functions
879-- Deprecated functions
829--------------------------------------------------------------------------------
880--------------------------------------------------------------------------------
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 @@
12{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeOperators #-}
13{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE UndecidableInstances #-}
14{-# LANGUAGE UndecidableInstances #-}
15+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15-- | This module contains various functions for working with 'Dynamic' values.
16-- | This module contains various functions for working with 'Dynamic' values.
16-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
17-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
17module Reflex.Dynamic
18module Reflex.Dynamic
@@ -38,8 +39,8 @@ module Reflex.Dynamic
38 , traceDyn
39 , traceDyn
39 , traceDynWith
40 , traceDynWith
40 , splitDynPure
41 , splitDynPure
41− , distributeDMapOverDynPure
42 , distributeMapOverDynPure
42 , distributeMapOverDynPure
43+ , distributeDMapOverDynPure
43 , distributeListOverDynPure
44 , distributeListOverDynPure
44 , Demux
45 , Demux
45 , demux
46 , demux
src/Reflex/Dynamic/TH.hs+1
@@ -4,6 +4,7 @@
4{-# LANGUAGE ScopedTypeVariables #-}
4{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE TemplateHaskell #-}
5{-# LANGUAGE TemplateHaskell #-}
6{-# LANGUAGE TypeOperators #-}
6{-# LANGUAGE TypeOperators #-}
7+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
7-- | Template Haskell helper functions for building complex 'Dynamic' values.
8-- | Template Haskell helper functions for building complex 'Dynamic' values.
8module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
9module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
9
10
src/Reflex/Dynamic/Uniq.hs+1
@@ -1,6 +1,7 @@
1{-# LANGUAGE FlexibleInstances #-}
1{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE MagicHash #-}
2{-# LANGUAGE MagicHash #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
4-- | This module provides a variation of 'Dynamic' values that uses cheap
5-- | This module provides a variation of 'Dynamic' values that uses cheap
5-- pointer equality checks to reduce the amount of signal propagation needed.
6-- pointer equality checks to reduce the amount of signal propagation needed.
6module Reflex.Dynamic.Uniq
7module Reflex.Dynamic.Uniq
src/Reflex/DynamicWriter.hs+69−52
@@ -9,83 +9,59 @@
9{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
11{-# LANGUAGE UndecidableInstances #-}
11{-# LANGUAGE UndecidableInstances #-}
12+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12module Reflex.DynamicWriter where
13module Reflex.DynamicWriter where
13
14
14import Control.Monad.Exception
15import Control.Monad.Exception
16+import Control.Monad.Identity
15import Control.Monad.IO.Class
17import Control.Monad.IO.Class
16import Control.Monad.Reader
18import Control.Monad.Reader
17import Control.Monad.Ref
19import Control.Monad.Ref
18import Control.Monad.State.Strict
20import Control.Monad.State.Strict
19−import Data.Dependent.Map (DMap)
21+import Data.Dependent.Map (DMap, DSum (..))
20−import Data.Functor.Compose
22+import qualified Data.Dependent.Map as DMap
21import Data.Functor.Misc
23import Data.Functor.Misc
22import Data.Map (Map)
24import Data.Map (Map)
23import qualified Data.Map as Map
25import qualified Data.Map as Map
24import Data.Semigroup
26import Data.Semigroup
27+import Data.Some (Some)
28+import qualified Data.Some as Some
25import Reflex.Class
29import Reflex.Class
26−import Reflex.Deletable.Class
27import Reflex.Dynamic
30import Reflex.Dynamic
31+import Reflex.Host.Class
28import Reflex.PerformEvent.Class
32import Reflex.PerformEvent.Class
29import Reflex.PostBuild.Class
33import Reflex.PostBuild.Class
30−import Reflex.Host.Class
31
34
32instance MonadTrans (DynamicWriterT t w) where
35instance MonadTrans (DynamicWriterT t w) where
33 lift = DynamicWriterT . lift
36 lift = DynamicWriterT . lift
34
37
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)
63mconcatIncremental m0 e = do
39mconcatIncremental m0 e = do
64 d <- foldDynMaybe apply m0 e
40 d <- foldDynMaybe apply m0 e
65 return $ fmap (mconcat . Map.elems) d
41 return $ fmap (mconcat . Map.elems) d
66
42
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')
68mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
44mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
69
45
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')
71mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
47mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
72
48
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'
74unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
50unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
75
51
76−incrementalExtractFunctorDMap :: Reflex t => Incremental t PatchMap (Map k (f v)) -> Incremental t PatchDMap (DMap (Const2 k v) f)
52+incrementalExtractFunctorDMap :: (Reflex t, Ord k) => Incremental t (PatchMap k (f v)) -> Incremental t (PatchDMap (Const2 k v) f)
77−incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap Compose m
53+incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap ComposeMaybe m
78
54
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)
80mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
56mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
81
57
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))
83holdIncrementalReplaceableMap m0 m' = do
59holdIncrementalReplaceableMap m0 m' = do
84 rec vals <- holdIncremental m0 $ m' <> valChanges
60 rec vals <- holdIncremental m0 $ m' <> valChanges
85 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
61 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
86 return $ mapIncrementalMapValues _replaceable_value vals
62 return $ mapIncrementalMapValues _replaceable_value vals
87
63
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)
89mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
65mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
90 where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
66 where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
91 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
67 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
@@ -95,7 +71,7 @@ data Replaceable t w = Replaceable
95 , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
71 , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
96 }
72 }
97
73
98−type DynamicWriterAccumulator t w = [Replaceable t (Dynamic t w)]
74+type DynamicWriterAccumulator t w = [Dynamic t w]
99
75
100newtype 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
76newtype 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
101
77
@@ -115,7 +91,12 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWrit
115runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
91runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
116runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
92runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
117
93
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))
119mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
100mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
120 rec vals <- holdIncremental m0 $ m' <> valChanges
101 rec vals <- holdIncremental m0 $ m' <> valChanges
121 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
102 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
@@ -130,17 +111,16 @@ mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
130 result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
111 result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
131 return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
112 return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
132
113
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)
134runDynamicWriterT (DynamicWriterT a) = do
115runDynamicWriterT (DynamicWriterT a) = do
135 (result, ws) <- runStateT a []
116 (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)
138
118
139class Monad m => MonadDynamicWriter t w m | m -> t w where
119class Monad m => MonadDynamicWriter t w m | m -> t w where
140 tellDyn :: Dynamic t w -> m ()
120 tellDyn :: Dynamic t w -> m ()
141
121
142instance (Monad m, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
122instance (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 :)
144
124
145instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
125instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
146 ask = lift ask
126 ask = lift ask
@@ -154,12 +134,6 @@ liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
154 put newS
134 put newS
155 return (b, a)
135 return (b, a)
156
136
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−
163instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
137instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
164 type Performable (DynamicWriterT t w m) = Performable m
138 type Performable (DynamicWriterT t w m) = Performable m
165 performEvent_ = lift . performEvent_
139 performEvent_ = lift . performEvent_
@@ -179,3 +153,46 @@ instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
179instance MonadState s m => MonadState s (DynamicWriterT t w m) where
153instance MonadState s m => MonadState s (DynamicWriterT t w m) where
180 get = lift get
154 get = lift get
181 put = lift . put
155 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 #-}
1{-# LANGUAGE ExistentialQuantification #-}
2{-# LANGUAGE ExistentialQuantification #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
@@ -9,6 +10,7 @@
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE TypeFamilies #-}
11{-# LANGUAGE TypeFamilies #-}
11{-# LANGUAGE UndecidableInstances #-}
12{-# LANGUAGE UndecidableInstances #-}
13+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12-- | This module provides the interface for hosting 'Reflex' engines. This
14-- | This module provides the interface for hosting 'Reflex' engines. This
13-- should only be necessary if you're writing a binding or some other library
15-- should only be necessary if you're writing a binding or some other library
14-- that provides a core event loop.
16-- that provides a core event loop.
@@ -43,9 +45,20 @@ import Data.Dependent.Sum (DSum (..))
43import Data.GADT.Compare
45import Data.GADT.Compare
44import Data.Monoid
46import Data.Monoid
45
47
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+
46-- Note: this import must come last to silence warnings from AMP
55-- Note: this import must come last to silence warnings from AMP
47import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
56import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
48
57
58+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
59+#include "../Spider/SpiderTimelineHost.include.hs"
60+#endif
61+
49-- | Framework implementation support class for the reflex implementation
62-- | Framework implementation support class for the reflex implementation
50-- represented by @t@.
63-- represented by @t@.
51class ( Reflex t
64class ( 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 @@
1{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE GADTs #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE PartialTypeSignatures #-}
7+{-# LANGUAGE PolyKinds #-}
5{-# LANGUAGE RankNTypes #-}
8{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RecursiveDo #-}
9{-# LANGUAGE RecursiveDo #-}
7{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
@@ -9,34 +12,39 @@
9{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeOperators #-}
13{-# LANGUAGE TypeOperators #-}
11{-# LANGUAGE UndecidableInstances #-}
14{-# LANGUAGE UndecidableInstances #-}
15+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12module Reflex.PerformEvent.Base where
16module Reflex.PerformEvent.Base where
13
17
14import Reflex.Class
18import Reflex.Class
15−import Reflex.Deletable.Class
16import Reflex.Host.Class
19import Reflex.Host.Class
17import Reflex.PerformEvent.Class
20import Reflex.PerformEvent.Class
21+import Reflex.PostBuild.Class
18
22
19import Control.Lens
23import Control.Lens
20import Control.Monad.Exception
24import Control.Monad.Exception
21import Control.Monad.Identity
25import Control.Monad.Identity
26+import Control.Monad.Primitive
22import Control.Monad.Ref
27import Control.Monad.Ref
23import Control.Monad.State.Strict
28import Control.Monad.State.Strict
24−import Data.Align
29+import Control.Monad.Reader
30+import Data.Coerce
25−import Data.Dependent.Map (DMap)
31+import Data.Dependent.Map (DMap, GCompare (..), Some)
26import qualified Data.Dependent.Map as DMap
32import qualified Data.Dependent.Map as DMap
27import Data.Dependent.Sum
33import Data.Dependent.Sum
28−import Data.Functor.Compose
29import Data.Functor.Misc
34import Data.Functor.Misc
35+import qualified Data.List.NonEmpty as NonEmpty
30import Data.Maybe
36import Data.Maybe
31import Data.Semigroup
37import Data.Semigroup
38+import qualified Data.Some as Some
32import Data.These
39import Data.These
33−import Data.Word
40+import Data.Tuple
41+import Data.Unique.Tag
34
42
35newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
43newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
36
44
37newtype 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?
45newtype 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?
38
46
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 }
40
48
41deriving instance ReflexHost t => Functor (PerformEventT t m)
49deriving instance ReflexHost t => Functor (PerformEventT t m)
42deriving instance ReflexHost t => Applicative (PerformEventT t m)
50deriving instance ReflexHost t => Applicative (PerformEventT t m)
@@ -45,29 +53,31 @@ deriving instance ReflexHost t => MonadFix (PerformEventT t m)
45deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
53deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
46deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
54deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
47
55
48−instance (ReflexHost t, MonadAsyncException (HostFrame t)) => MonadAsyncException (PerformEventT t m) where
56+instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m) where
49− mask f = PerformEventT $ mask $ \unmask -> unPerformEventT $ f $ PerformEventT . unmask . unPerformEventT
57+ type PrimState (PerformEventT t m) = PrimState (HostFrame t)
58+ primitive = PerformEventT . lift . primitive
50
59
51−instance (ReflexHost t, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEvent t (PerformEventT t m) where
60+instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent t (PerformEventT t m) where
52− type Performable (PerformEventT t m) = PerformEventT t m
61+ type Performable (PerformEventT t m) = HostFrame t
53 {-# INLINABLE performEvent_ #-}
62 {-# INLINABLE performEvent_ #-}
54− performEvent_ = PerformEventT . modify . (:) . fmap (This . (>> return []))
63+ performEvent_ = PerformEventT . requesting_
55 {-# INLINABLE performEvent #-}
64 {-# INLINABLE performEvent #-}
56− performEvent e = PerformEventT $ do
65+ performEvent = PerformEventT . fmap (fmap runIdentity) . requesting
57− (eResult, reResultTrigger) <- newEventWithTriggerRef
66+
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
67+instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
64− {-# INLINABLE deletable #-}
68+ runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequestTWith f (coerce outerA0) (coerceEvent outerA')
65− deletable d a = PerformEventT $ do
69+ where f :: HostFrame t a -> Event t (HostFrame t b) -> RequestT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
66− (result, reverseEventsToPerform) <- lift $ runStateT (unPerformEventT a) []
70+ f a0 a' = do
67− let (numberedEvents, _) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
71+ result0 <- lift a0
68− eventToPerform <- lift $ mergePerformEventsAndAdd (DMap.fromList numberedEvents) never
72+ result' <- requesting a'
69− modify (align eventToPerform d :)
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
70− return result
80+ return (result0, fmap runIdentity result')
71
81
72instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
82instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
73 {-# INLINABLE newEventWithTrigger #-}
83 {-# INLINABLE newEventWithTrigger #-}
@@ -75,55 +85,19 @@ instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
75 {-# INLINABLE newFanEventWithTrigger #-}
85 {-# INLINABLE newFanEventWithTrigger #-}
76 newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
86 newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
77
87
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−
123{-# INLINABLE hostPerformEventT #-}
88{-# 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)
125hostPerformEventT a = do
98hostPerformEventT a = do
99+ (response, responseTrigger) <- newEventWithTriggerRef
126− (result, eventToPerform) <- runHostFrame $ runPerformEventT a
100+ (result, eventToPerform) <- runHostFrame $ runRequestT (unPerformEventT a) response
127 eventToPerformHandle <- subscribeEvent eventToPerform
101 eventToPerformHandle <- subscribeEvent eventToPerform
128 return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
102 return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
129 let go :: [DSum (EventTrigger t) Identity] -> m [a']
103 let go :: [DSum (EventTrigger t) Identity] -> m [a']
@@ -135,11 +109,12 @@ hostPerformEventT a = do
135 case mToPerform of
109 case mToPerform of
136 Nothing -> return [result']
110 Nothing -> return [result']
137 Just toPerform -> do
111 Just toPerform -> do
138− followupEventTriggerRefs <- runHostFrame toPerform
112+ responses <- runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
139− followupEventTriggers <- forM followupEventTriggerRefs $ \(EventTriggerRef rt :=> x) -> do
113+ mrt <- readRef responseTrigger
140− mt <- readRef rt
114+ let followupEventTriggers = case mrt of
141− return $ fmap (:=> x) mt
115+ Just rt -> [rt :=> Identity responses]
116+ Nothing -> []
142− fmap (result':) $ go $ catMaybes followupEventTriggers
117+ (result':) <$> go followupEventTriggers
143 go triggers
118 go triggers
144
119
145instance ReflexHost t => MonadSample t (PerformEventT t m) where
120instance ReflexHost t => MonadSample t (PerformEventT t m) where
@@ -166,3 +141,177 @@ instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m)
166instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
141instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
167 {-# INLINABLE atomicModifyRef #-}
142 {-# INLINABLE atomicModifyRef #-}
168 atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
143 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 @@
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE UndecidableInstances #-}
9{-# LANGUAGE UndecidableInstances #-}
10+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10module Reflex.PerformEvent.Class where
11module Reflex.PerformEvent.Class where
11
12
12import Reflex.Class
13import Reflex.Class
13
14
15+import Control.Monad.Identity
14import Control.Monad.Reader
16import Control.Monad.Reader
17+import qualified Control.Monad.State.Lazy as Lazy
18+import Control.Monad.State.Strict
15
19
16class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
20class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
17 type Performable m :: * -> *
21 type Performable m :: * -> *
@@ -24,6 +28,11 @@ class Monad m => TriggerEvent t m | m -> t where
24 newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
28 newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
25 newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
29 newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
26
30
31+class (Reflex t, Monad m) => MonadRequest t m | m -> t where
32+ type Request m :: * -> *
33+ type Response m :: * -> *
34+ withRequesting :: (Event t (Response m a) -> m (Event t (Request m a), r)) -> m r
35+
27{-# INLINABLE performEventAsync #-}
36{-# INLINABLE performEventAsync #-}
28performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
37performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
29performEventAsync e = do
38performEventAsync e = do
@@ -44,3 +53,41 @@ instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
44 newTriggerEvent = lift newTriggerEvent
53 newTriggerEvent = lift newTriggerEvent
45 newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
54 newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
46 newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
55 newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
56+
57+instance MonadRequest t m => MonadRequest t (ReaderT r m) where
58+ type Request (ReaderT r m) = Request m
59+ type Response (ReaderT r m) = Response m
60+ withRequesting f = do
61+ r <- ask
62+ lift $ withRequesting $ (`runReaderT` r) . f
63+
64+instance MonadRequest t m => MonadRequest t (StateT s m) where
65+ type Request (StateT s m) = Request m
66+ type Response (StateT s m) = Response m
67+ withRequesting f = do
68+ old <- get
69+ (result, new) <- lift $ withRequesting $ \x -> do
70+ ((request, result), new) <- runStateT (f x) old
71+ return (request, (result, new))
72+ put new
73+ return result
74+
75+instance MonadRequest t m => MonadRequest t (Lazy.StateT s m) where
76+ type Request (Lazy.StateT s m) = Request m
77+ type Response (Lazy.StateT s m) = Response m
78+ withRequesting f = do
79+ old <- Lazy.get
80+ (result, new) <- lift $ withRequesting $ \x -> do
81+ ((request, result), new) <- Lazy.runStateT (f x) old
82+ return (request, (result, new))
83+ Lazy.put new
84+ return result
85+
86+requesting :: MonadRequest t m => Event t (Request m a) -> m (Event t (Response m a))
87+requesting req = withRequesting $ return . (,) req
88+
89+requesting_ :: MonadRequest t m => Event t (Request m a) -> m ()
90+requesting_ req = withRequesting $ \_ -> return (req, ())
91+
92+requestingIdentity :: (MonadRequest t m, Response m ~ Identity) => Event t (Request m a) -> m (Event t a)
93+requestingIdentity = fmap (fmap runIdentity) . requesting
src/Reflex/PostBuild/Class.hs+25−5
@@ -3,18 +3,21 @@
3{-# LANGUAGE FunctionalDependencies #-}
3{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RecursiveDo #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE UndecidableInstances #-}
10{-# LANGUAGE UndecidableInstances #-}
11+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10module Reflex.PostBuild.Class where
12module Reflex.PostBuild.Class where
11
13
12import Control.Monad.Exception
14import Control.Monad.Exception
15+import Control.Monad.Primitive
13import Control.Monad.Reader
16import Control.Monad.Reader
14import Control.Monad.Ref
17import Control.Monad.Ref
15import Control.Monad.Trans.Control
18import Control.Monad.Trans.Control
19+import qualified Data.Dependent.Map as DMap
16import Reflex.Class
20import Reflex.Class
17−import Reflex.Deletable.Class
18import Reflex.Host.Class
21import Reflex.Host.Class
19import Reflex.PerformEvent.Class
22import Reflex.PerformEvent.Class
20
23
@@ -23,6 +26,10 @@ class (Reflex t, Monad m) => PostBuild t m | m -> t where
23
26
24newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
27newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
25
28
29+instance PrimMonad m => PrimMonad (PostBuildT x m) where
30+ type PrimState (PostBuildT x m) = PrimState m
31+ primitive = lift . primitive
32+
26instance MonadTransControl (PostBuildT t) where
33instance MonadTransControl (PostBuildT t) where
27 type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
34 type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
28 {-# INLINABLE liftWith #-}
35 {-# INLINABLE liftWith #-}
@@ -34,10 +41,6 @@ instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
34 {-# INLINABLE getPostBuild #-}
41 {-# INLINABLE getPostBuild #-}
35 getPostBuild = PostBuildT ask
42 getPostBuild = PostBuildT ask
36
43
37−instance Deletable t m => Deletable t (PostBuildT t m) where
38− {-# INLINABLE deletable #-}
39− deletable = liftThrough . deletable
40−
41instance MonadSample t m => MonadSample t (PostBuildT t m) where
44instance MonadSample t m => MonadSample t (PostBuildT t m) where
42 {-# INLINABLE sample #-}
45 {-# INLINABLE sample #-}
43 sample = lift . sample
46 sample = lift . sample
@@ -89,3 +92,20 @@ runPostBuildT (PostBuildT a) = runReaderT a
89
92
90instance PostBuild t m => PostBuild t (ReaderT r m) where
93instance PostBuild t m => PostBuild t (ReaderT r m) where
91 getPostBuild = lift getPostBuild
94 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 @@
5{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
8
9
9-- There are two expected orphan instances in this module:
10-- There are two expected orphan instances in this module:
10-- * MonadSample (Pure t) ((->) t)
11-- * MonadSample (Pure t) ((->) t)
@@ -28,6 +29,7 @@ import Data.Functor.Identity
28import Data.Maybe
29import Data.Maybe
29import Data.MemoTrie
30import Data.MemoTrie
30import Data.Monoid
31import Data.Monoid
32+import Data.Type.Coercion
31import Reflex.Class
33import Reflex.Class
32
34
33data Pure t
35data Pure t
@@ -40,7 +42,7 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
40 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
42 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
41 newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
43 newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
42 newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
44 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) }
44
46
45 type PushM (Pure t) = (->) t
47 type PushM (Pure t) = (->) t
46 type PullM (Pure t) = (->) t
48 type PullM (Pure t) = (->) t
@@ -109,6 +111,9 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
109 Nothing -> Nothing
111 Nothing -> Nothing
110 Just patch -> apply patch old
112 Just patch -> apply patch old
111 in (old, e)
113 in (old, e)
114+ behaviorCoercion Coercion = Coercion
115+ eventCoercion Coercion = Coercion
116+ dynamicCoercion Coercion = Coercion
112
117
113instance Functor (Dynamic (Pure t)) where
118instance Functor (Dynamic (Pure t)) where
114 fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
119 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
152 let Behavior f = hold initialValue e initialTime
157 let Behavior f = hold initialValue e initialTime
153 in Dynamic $ \t -> (f t, unEvent e t)
158 in Dynamic $ \t -> (f t, unEvent e t)
154
159
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
156 holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
161 holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
157 where f = memo $ \sampleTime ->
162 where f = memo $ \sampleTime ->
158 -- Really, the sampleTime should never be prior to the initialTime,
163 -- Really, the sampleTime should never be prior to the initialTime,
src/Reflex/Spider.hs+4−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1-- | This module exports all of the user-facing functionality of the 'Spider'
2-- | This module exports all of the user-facing functionality of the 'Spider'
2-- 'Reflex' engine
3-- 'Reflex' engine
3module Reflex.Spider
4module Reflex.Spider
@@ -9,9 +10,11 @@ module Reflex.Spider
9 , runSpiderHostForTimeline
10 , runSpiderHostForTimeline
10 , newSpiderTimeline
11 , newSpiderTimeline
11 , withSpiderTimeline
12 , withSpiderTimeline
12− , askSpiderTimeline
13 -- * Deprecated
13 -- * Deprecated
14 , SpiderEnv
14 , SpiderEnv
15 ) where
15 ) where
16
16
17+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
18+import Reflex.Class
19+#endif
17import Reflex.Spider.Internal
20import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hs+1000−1260
@@ -2,7 +2,6 @@
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE ConstraintKinds #-}
4{-# LANGUAGE EmptyDataDecls #-}
4{-# LANGUAGE EmptyDataDecls #-}
5−{-# LANGUAGE ExistentialQuantification #-}
6{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
7{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE FunctionalDependencies #-}
@@ -18,6 +17,10 @@
18{-# LANGUAGE TypeFamilies #-}
17{-# LANGUAGE TypeFamilies #-}
19{-# LANGUAGE TypeOperators #-}
18{-# LANGUAGE TypeOperators #-}
20{-# LANGUAGE UndecidableInstances #-}
19{-# LANGUAGE UndecidableInstances #-}
20+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
21+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
22+{-# OPTIONS_GHC -fno-warn-orphans #-}
23+#endif
21-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
24-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
22-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
25-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
23module Reflex.Spider.Internal where
26module Reflex.Spider.Internal where
@@ -31,46 +34,57 @@ import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
31import Control.Monad.Primitive
34import Control.Monad.Primitive
32import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
35import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
33import Control.Monad.Ref
36import Control.Monad.Ref
34−import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
35import Data.Align
37import Data.Align
36import Data.Coerce
38import Data.Coerce
37import Data.Dependent.Map (DMap, DSum (..))
39import Data.Dependent.Map (DMap, DSum (..))
38import qualified Data.Dependent.Map as DMap
40import qualified Data.Dependent.Map as DMap
39import Data.Foldable hiding (concat, elem, sequence_)
41import Data.Foldable hiding (concat, elem, sequence_)
40import Data.Function
42import Data.Function
41−import Data.Functor.Compose
43+import Data.Functor.Constant
44+import Data.Functor.Misc
45+import Data.Functor.Product
42import Data.GADT.Compare
46import Data.GADT.Compare
43import Data.IntMap.Strict (IntMap)
47import Data.IntMap.Strict (IntMap)
44import qualified Data.IntMap.Strict as IntMap
48import qualified Data.IntMap.Strict as IntMap
45import Data.IORef
49import Data.IORef
46import Data.List hiding (foldl')
50import Data.List hiding (foldl')
47−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
48−import qualified Data.List.NonEmpty as NonEmpty
49import Data.Maybe
51import Data.Maybe
50−import Data.Monoid (mempty, (<>))
52+import Data.Monoid ((<>))
53+import Data.Proxy
51import Data.Some (Some)
54import Data.Some (Some)
52import qualified Data.Some as Some
55import qualified Data.Some as Some
53import Data.These
56import Data.These
54import Data.Traversable
57import Data.Traversable
55−import Data.Tree (Forest, Tree (..), drawForest)
56import Data.Word
58import Data.Word
59+import GHC.Exts
57import GHC.IORef (IORef (..))
60import GHC.IORef (IORef (..))
58import GHC.Stack
61import GHC.Stack
62+import Reflex.FunctorMaybe
59import System.IO.Unsafe
63import System.IO.Unsafe
60import System.Mem.Weak
64import System.Mem.Weak
61import Unsafe.Coerce
65import Unsafe.Coerce
62
66
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+
63import Data.WeakBag (WeakBag, WeakBagTicket)
75import Data.WeakBag (WeakBag, WeakBagTicket)
64import qualified Data.WeakBag as WeakBag
76import qualified Data.WeakBag as WeakBag
77+#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
78+import Data.Type.Coercion
79+import qualified Reflex.Class
65import qualified Reflex.Class as R
80import 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
67
84
68-- Note: must come last to silence warnings due to AMP on GHC < 7.10
85-- Note: must come last to silence warnings due to AMP on GHC < 7.10
69import Prelude hiding (any, concat, mapM, mapM_, sequence)
86import Prelude hiding (any, concat, mapM, mapM_, sequence)
70
87
71−type SubscriberList a = WeakBag (Subscriber a)
72−type SubscriberListTicket a = WeakBagTicket (Subscriber a)
73−
74debugPropagate :: Bool
88debugPropagate :: Bool
75
89
76debugInvalidateHeight :: Bool
90debugInvalidateHeight :: Bool
@@ -90,35 +104,32 @@ debugInvalidate = True
90class HasNodeId a where
104class HasNodeId a where
91 getNodeId :: a -> Int
105 getNodeId :: a -> Int
92
106
93−instance HasNodeId (Hold p a) where
107+instance HasNodeId (Hold x p a) where
94 getNodeId = holdNodeId
108 getNodeId = holdNodeId
95
109
96−instance HasNodeId (PushSubscribed a b) where
110+instance HasNodeId (PushSubscribed x a b) where
97 getNodeId = pushSubscribedNodeId
111 getNodeId = pushSubscribedNodeId
98
112
99−instance HasNodeId (SwitchSubscribed a) where
113+instance HasNodeId (SwitchSubscribed x a) where
100 getNodeId = switchSubscribedNodeId
114 getNodeId = switchSubscribedNodeId
101
115
102−instance HasNodeId (MergeSubscribed a) where
103− getNodeId = mergeSubscribedNodeId
104−
105−instance HasNodeId (FanSubscribed a) where
116+instance HasNodeId (FanSubscribed x a) where
106 getNodeId = fanSubscribedNodeId
117 getNodeId = fanSubscribedNodeId
107
118
108−instance HasNodeId (CoincidenceSubscribed a) where
119+instance HasNodeId (CoincidenceSubscribed x a) where
109 getNodeId = coincidenceSubscribedNodeId
120 getNodeId = coincidenceSubscribedNodeId
110
121
111−instance HasNodeId (RootSubscribed a) where
122+instance HasNodeId (RootSubscribed x a) where
112 getNodeId = rootSubscribedNodeId
123 getNodeId = rootSubscribedNodeId
113
124
114−instance HasNodeId (Pull a) where
125+instance HasNodeId (Pull x a) where
115 getNodeId = pullNodeId
126 getNodeId = pullNodeId
116
127
117{-# INLINE showNodeId #-}
128{-# INLINE showNodeId #-}
118showNodeId :: HasNodeId a => a -> String
129showNodeId :: HasNodeId a => a -> String
119showNodeId = ("#"<>) . show . getNodeId
130showNodeId = ("#"<>) . show . getNodeId
120
131
121−showSubscriberType :: Subscriber a -> String
132+showSubscriberType :: Subscriber x a -> String
122showSubscriberType = \case
133showSubscriberType = \case
123 SubscriberPush _ _ -> "SubscriberPush"
134 SubscriberPush _ _ -> "SubscriberPush"
124 SubscriberMerge _ _ -> "SubscriberMerge"
135 SubscriberMerge _ _ -> "SubscriberMerge"
@@ -131,7 +142,7 @@ showSubscriberType = \case
131 SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
142 SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
132 SubscriberHandle -> "SubscriberHandle"
143 SubscriberHandle -> "SubscriberHandle"
133
144
134−showEventType :: Event a -> String
145+showEventType :: Event x a -> String
135showEventType = \case
146showEventType = \case
136 EventRoot _ _ -> "EventRoot"
147 EventRoot _ _ -> "EventRoot"
137 EventNever -> "EventNever"
148 EventNever -> "EventNever"
@@ -179,153 +190,359 @@ unsafeNodeId a = unsafePerformIO $ do
179-- Event
190-- Event
180--------------------------------------------------------------------------------
191--------------------------------------------------------------------------------
181
192
182−data Event a
193+newtype Event x a = Event { unEvent :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
183− = forall k. GCompare k => EventRoot !(k a) !(Root k)
194+
184− | EventNever
195+{-# INLINE subscribeAndRead #-}
185− | forall b. EventPush !(Push b a)
196+subscribeAndRead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
186− | forall k. (GCompare k, a ~ DMap k Identity) => EventMerge !(Merge k)
197+subscribeAndRead = unEvent
187− | forall k. GCompare k => EventFan !(k a) !(Fan k)
198+
188− | EventSwitch !(Switch a)
199+{-# RULES
189− | EventCoincidence !(Coincidence a)
200+"cacheEvent/cacheEvent" forall e. cacheEvent (cacheEvent e) = cacheEvent e
190− | forall p b. (a ~ p b, R.Patch p) => EventHold !(Hold p b)
201+"cacheEvent/pushCheap" forall f e. pushCheap f (cacheEvent e) = cacheEvent (pushCheap f e)
191− | forall p b. (a ~ p b, R.Patch p) => EventDyn !(Dyn p b)
202+"hold/cacheEvent" forall f e. hold f (cacheEvent e) = hold f e
192− | forall b. (a ~ Identity b) => EventHoldIdentity !(Hold Identity b)
203+ #-}
193− | forall b. (a ~ Identity b) => EventDynIdentity !(Dyn Identity b)
204+{- RULES
194−
205+ "subscribeAndRead/cacheEvent" forall e. subscribeAndRead (cacheEvent e) = subscribeAndRead e
195−{-# SPECIALIZE subscribe :: Event a -> Subscriber a -> EventM x (EventSubscription a) #-}
206+ #-}
196−subscribe :: forall x m a. CanSubscribe x m => Event a -> Subscriber a -> m (EventSubscription a)
207+
197−subscribe e sub = case e of
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
198− EventRoot k r -> wrap EventSubscribedRoot $ liftIO . getRootSubscribed k r
294+eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed k r
199− EventNever -> return $ EventSubscription Nothing EventSubscribedNever
295+
200− EventPush p -> wrap EventSubscribedPush $ getPushSubscribed p
296+eventNever :: Event x a
201− EventFan k f -> wrap (EventSubscribedFan k) $ getFanSubscribed k f
297+eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
202− EventMerge m -> wrap EventSubscribedMerge $ getMergeSubscribed m
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
203− EventSwitch s -> wrap EventSubscribedSwitch $ getSwitchSubscribed s
303+eventSwitch !s = Event $ wrap eventSubscribedSwitch $ getSwitchSubscribed s
204− EventCoincidence c -> wrap EventSubscribedCoincidence $ getCoincidenceSubscribed c
304+
205− EventHold h -> subscribeHoldEvent h sub
305+eventCoincidence :: HasSpiderTimeline x => Coincidence x a -> Event x a
206− EventDyn j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
306+eventCoincidence !c = Event $ wrap eventSubscribedCoincidence $ getCoincidenceSubscribed c
207− EventHoldIdentity h -> subscribeHoldEvent h sub
307+
308+eventHold :: HasSpiderTimeline x => Hold x p -> Event x p
208− EventDynIdentity j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
309+eventHold !h = Event $ subscribeHoldEvent h
209− where
310+
210− wrap :: (t -> EventSubscribed a) -> (Subscriber a -> m (SubscriberListTicket a, t)) -> m (EventSubscription a)
311+eventDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Event x p
211− wrap tag getSpecificSubscribed = do
312+eventDyn !j = Event $ \sub -> getDynHold j >>= \h -> subscribeHoldEvent h sub
212− (sln, subd) <- getSpecificSubscribed sub
213− return $ EventSubscription (Just sln) $ tag subd
214
313
215{-# INLINE subscribeCoincidenceInner #-}
314{-# 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)
217subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
316subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
218 subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
317 subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
219− subscription@(EventSubscription _ innerSubd) <- subscribe inner subInner
318+ (subscription@(EventSubscription _ innerSubd), innerOcc) <- subscribeAndRead inner subInner
220− innerOcc <- liftIO $ readEventSubscribed innerSubd
221 innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
319 innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
222 let height = max innerHeight outerHeight
320 let height = max innerHeight outerHeight
223 defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
321 defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
224 return (innerOcc, height, innerSubd)
322 return (innerOcc, height, innerSubd)
225
323
324+--------------------------------------------------------------------------------
325+-- Subscriber
326+--------------------------------------------------------------------------------
327+
328+data Subscriber x a = Subscriber
329+ { subscriberPropagate :: !(a -> EventM x ())
330+ , subscriberInvalidateHeight :: !(Height -> IO ())
331+ , subscriberRecalculateHeight :: !(Height -> IO ())
332+ }
333+
334+--TODO: Move this comment to WeakBag
335+-- These function are constructor functions that are marked NOINLINE so they are
336+-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
337+-- so any weak references that are attached to the constructors will have their
338+-- finalizer run. Using the opaque constructor, does not see the
339+-- constructor application, so it behaves like an IORef and cannot be fused away.
340+--
341+-- The result is also evaluated to WHNF, since forcing a thunk invalidates
342+-- the weak pointer to it in some cases.
343+
344+newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
345+newSubscriberHold h = return $ Subscriber
346+ { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
347+ , subscriberInvalidateHeight = \_ -> return ()
348+ , subscriberRecalculateHeight = \_ -> return ()
349+ }
350+
351+newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
352+newSubscriberFan subscribed = return $ Subscriber
353+ { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
354+ subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
355+ tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
356+ liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
357+ scheduleClear $ fanSubscribedOccurrence subscribed
358+ let f _ (Pair (Identity v) subsubs) = do
359+ propagate v $ _fanSubscribedChildren_list subsubs
360+ return $ Constant ()
361+ _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
362+ return ()
363+ , subscriberInvalidateHeight = \old -> do
364+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
365+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
366+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
367+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
368+ , subscriberRecalculateHeight = \new -> do
369+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
370+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
371+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
372+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
373+ }
374+
375+newSubscriberSwitch :: forall x a. HasSpiderTimeline x => SwitchSubscribed x a -> IO (Subscriber x a)
376+newSubscriberSwitch subscribed = return $ Subscriber
377+ { subscriberPropagate = \a -> {-# SCC "traverseSwitch" #-} do
378+ tracePropagate (Proxy :: Proxy x) $ "SubscriberSwitch" <> showNodeId subscribed
379+ liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
380+ scheduleClear $ switchSubscribedOccurrence subscribed
381+ propagate a $ switchSubscribedSubscribers subscribed
382+ , subscriberInvalidateHeight = \_ -> do
383+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
384+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
385+ when (oldHeight /= invalidHeight) $ do
386+ writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
387+ WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
388+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
389+ , subscriberRecalculateHeight = \new -> do
390+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
391+ updateSwitchHeight new subscribed
392+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
393+ }
394+
395+newSubscriberCoincidenceOuter :: forall x b. HasSpiderTimeline x => CoincidenceSubscribed x b -> IO (Subscriber x (Event x b))
396+newSubscriberCoincidenceOuter subscribed = return $ Subscriber
397+ { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceOuter" #-} do
398+ tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
399+ outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
400+ tracePropagate (Proxy :: Proxy x) $ " outerHeight = " <> show outerHeight
401+ (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
402+ tracePropagate (Proxy :: Proxy x) $ " isJust occ = " <> show (isJust occ)
403+ tracePropagate (Proxy :: Proxy x) $ " innerHeight = " <> show innerHeight
404+ liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
405+ scheduleClear $ coincidenceSubscribedInnerParent subscribed
406+ case occ of
407+ Nothing -> do
408+ when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
409+ writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
410+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
411+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
412+ Just o -> do -- Since it's already firing, no need to adjust height
413+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
414+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
415+ propagate o $ coincidenceSubscribedSubscribers subscribed
416+ , subscriberInvalidateHeight = \_ -> do
417+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
418+ invalidateCoincidenceHeight subscribed
419+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
420+ , subscriberRecalculateHeight = \_ -> do
421+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
422+ recalculateCoincidenceHeight subscribed
423+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
424+ }
425+
426+newSubscriberCoincidenceInner :: forall x a. HasSpiderTimeline x => CoincidenceSubscribed x a -> IO (Subscriber x a)
427+newSubscriberCoincidenceInner subscribed = return $ Subscriber
428+ { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceInner" #-} do
429+ tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceInner" <> showNodeId subscribed
430+ occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
431+ case occ of
432+ Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
433+ Nothing -> do
434+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
435+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
436+ propagate a $ coincidenceSubscribedSubscribers subscribed
437+ , subscriberInvalidateHeight = \_ -> do
438+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
439+ invalidateCoincidenceHeight subscribed
440+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
441+ , subscriberRecalculateHeight = \_ -> do
442+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
443+ recalculateCoincidenceHeight subscribed
444+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
445+ }
446+
447+invalidateSubscriberHeight :: Height -> Subscriber x a -> IO ()
448+invalidateSubscriberHeight = flip subscriberInvalidateHeight
449+
450+recalculateSubscriberHeight :: Height -> Subscriber x a -> IO ()
451+recalculateSubscriberHeight = flip subscriberRecalculateHeight
452+
453+-- | Propagate everything at the current height
454+propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
455+propagate a subscribers = withIncreasedDepth $ do
456+ -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
457+ --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
458+ WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
459+
226--------------------------------------------------------------------------------
460--------------------------------------------------------------------------------
227-- EventSubscribed
461-- EventSubscribed
228--------------------------------------------------------------------------------
462--------------------------------------------------------------------------------
229
463
464+toAny :: a -> Any
465+toAny = unsafeCoerce
466+
230−data EventSubscribed a
467+data EventSubscribed x = EventSubscribed
231− = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
468+ { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
232− | EventSubscribedNever
469+ , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
233− | forall b. EventSubscribedPush !(PushSubscribed b a)
470+#ifdef DEBUG_CYCLES
234− | forall k. (GCompare k, a ~ DMap k Identity) => EventSubscribedMerge !(MergeSubscribed k)
471+ , eventSubscribedGetParents :: !(IO [Some (EventSubscribed x)]) -- For debugging loops
235− | forall k. GCompare k => EventSubscribedFan !(k a) !(FanSubscribed k)
472+ , eventSubscribedHasOwnHeightRef :: !Bool
236− | EventSubscribedSwitch !(SwitchSubscribed a)
473+ , eventSubscribedWhoCreated :: !(IO [String])
237− | EventSubscribedCoincidence !(CoincidenceSubscribed a)
474+#endif
238−
475+ }
239−readEventSubscribed :: EventSubscribed a -> IO (Maybe a)
476+
240−readEventSubscribed = \case
477+eventSubscribedRoot :: RootSubscribed x a -> EventSubscribed x
241− EventSubscribedRoot rs -> readRootSubscribed rs
478+eventSubscribedRoot !r = EventSubscribed
242− EventSubscribedNever -> return Nothing
479+ { eventSubscribedHeightRef = zeroRef
243− EventSubscribedPush ps -> readPushSubscribed ps
480+ , eventSubscribedRetained = toAny r
244− EventSubscribedMerge ms -> readMergeSubscribed ms
481+#ifdef DEBUG_CYCLES
245− EventSubscribedFan k fs -> readFanSubscribed k fs
482+ , eventSubscribedGetParents = return []
246− EventSubscribedSwitch ss -> readSwitchSubscribed ss
483+ , eventSubscribedHasOwnHeightRef = False
247− EventSubscribedCoincidence cs -> readCoincidenceSubscribed cs
484+ , eventSubscribedWhoCreated = return ["root"]
248−
485+#endif
249−{-# INLINE subscribeEventSubscribed #-}
486+ }
250−subscribeEventSubscribed :: EventSubscribed a -> Subscriber a -> IO (Maybe (SubscriberListTicket a))
487+
251−subscribeEventSubscribed es sub = case es of
488+eventSubscribedNever :: EventSubscribed x
252− EventSubscribedRoot r -> do
489+eventSubscribedNever = EventSubscribed
253− traceSubscribe $ "subscribeEventSubscribed Root"
490+ { eventSubscribedHeightRef = zeroRef
254− Just <$> subscribeRootSubscribed r sub
491+ , eventSubscribedRetained = toAny ()
255− EventSubscribedNever -> do
492+#ifdef DEBUG_CYCLES
256− traceSubscribe $ "subscribeEventSubscribed Never"
493+ , eventSubscribedGetParents = return []
257− return Nothing
494+ , eventSubscribedHasOwnHeightRef = False
258− EventSubscribedPush subscribed -> do
495+ , eventSubscribedWhoCreated = return ["never"]
259− traceSubscribe $ "subscribeEventSubscribed Push"
496+#endif
260− Just <$> subscribePushSubscribed subscribed sub
497+ }
261− EventSubscribedFan k subscribed -> do
498+
262− traceSubscribe $ "subscribeEventSubscribed Fan"
499+eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
263− Just <$> subscribeFanSubscribed k subscribed sub
500+eventSubscribedFan !subscribed = EventSubscribed
264− EventSubscribedMerge subscribed -> do
501+ { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
265− traceSubscribe $ "subscribeEventSubscribed Merge"
502+ , eventSubscribedRetained = toAny subscribed
266− Just <$> subscribeMergeSubscribed subscribed sub
503+#ifdef DEBUG_CYCLES
267− EventSubscribedSwitch subscribed -> do
504+ , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
268− traceSubscribe $ "subscribeEventSubscribed Switch"
505+ , eventSubscribedHasOwnHeightRef = False
269− Just <$> subscribeSwitchSubscribed subscribed sub
506+ , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
270− EventSubscribedCoincidence subscribed -> do
507+#endif
271− traceSubscribe $ "subscribeEventSubscribed Coincidence"
508+ }
272− Just <$> subscribeCoincidenceSubscribed subscribed sub
509+
273−
510+eventSubscribedSwitch :: SwitchSubscribed x a -> EventSubscribed x
511+eventSubscribedSwitch !subscribed = EventSubscribed
512+ { eventSubscribedHeightRef = switchSubscribedHeight subscribed
513+ , eventSubscribedRetained = toAny subscribed
514+#ifdef DEBUG_CYCLES
274−{-# INLINE eventSubscribedGetParents #-}
515+ , eventSubscribedGetParents = do
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
516+ s <- readIORef $ switchSubscribedCurrentParent subscribed
286− return [Some.This $ _eventSubscription_subscribed s]
517+ return [Some.This $ _eventSubscription_subscribed s]
287− EventSubscribedCoincidence subscribed -> do
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
288− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
529+ innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
289− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
530+ let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
290− innerParents = maybeToList $ fmap Some.This innerSubscription
531+ innerParents = maybeToList $ fmap Some.This innerSubscription
291− return $ outerParent : innerParents
532+ return $ outerParent : innerParents
292−
533+ , eventSubscribedHasOwnHeightRef = True
534+ , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
535+#endif
536+ }
537+
293−getEventSubscribedHeight :: EventSubscribed a -> IO Height
538+getEventSubscribedHeight :: EventSubscribed x -> IO Height
294getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
539getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
295
540
296−{-# INLINE eventSubscribedHeightRef #-}
541+#ifdef DEBUG_CYCLES
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]
542+whoCreatedEventSubscribed :: EventSubscribed x -> IO [String]
308−whoCreatedEventSubscribed = \case
543+whoCreatedEventSubscribed = eventSubscribedWhoCreated
309− EventSubscribedRoot _ -> return ["root"]
544+
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]
545+walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
329walkInvalidHeightParents s0 = do
546walkInvalidHeightParents s0 = do
330 subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
547 subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
331 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
548 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
@@ -335,18 +552,115 @@ walkInvalidHeightParents s0 = do
335 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
552 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
336 forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
553 forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
337 return subscribers
554 return subscribers
555+#endif
556+
557+{-# INLINE subscribeHoldEvent #-}
558+subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
559+subscribeHoldEvent = subscribeAndRead . holdEvent
338
560
339--------------------------------------------------------------------------------
561--------------------------------------------------------------------------------
340-- EventSubscription
562-- EventSubscription
341--------------------------------------------------------------------------------
563--------------------------------------------------------------------------------
342
564
343−data EventSubscription a = EventSubscription
565+data EventSubscription x = EventSubscription
344− { _eventSubscription_ticket :: !(Maybe (SubscriberListTicket a))
566+ { _eventSubscription_unsubscribe :: !(IO ())
345− , _eventSubscription_subscribed :: !(EventSubscribed a)
567+ , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
346 }
568 }
347
569
348−unsubscribe :: EventSubscription a -> IO ()
570+unsubscribe :: EventSubscription x -> IO ()
349−unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
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
350
664
351--------------------------------------------------------------------------------
665--------------------------------------------------------------------------------
352-- Combinators
666-- Combinators
@@ -356,31 +670,56 @@ unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
356--representational so we can use coerce
670--representational so we can use coerce
357
671
358--type role Hold representational
672--type role Hold representational
359−data Hold p a
673+data Hold x p
360− = Hold { holdValue :: !(IORef a)
674+ = Hold { holdValue :: !(IORef (PatchTarget p))
361− , holdInvalidators :: !(IORef [Weak Invalidator])
675+ , holdInvalidators :: !(IORef [Weak (Invalidator x)])
676+ , holdEvent :: Event x p -- This must be lazy, or holds cannot be defined before their input Events
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
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
363#ifdef DEBUG_NODEIDS
678#ifdef DEBUG_NODEIDS
364 , holdNodeId :: Int
679 , holdNodeId :: Int
365#endif
680#endif
366 }
681 }
367
682
683+data Global
684+
685+{-# NOINLINE globalSpiderTimelineEnv #-}
686+globalSpiderTimelineEnv :: SpiderTimelineEnv Global
687+!globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
688+
689+-- | Stores all global data relevant to a particular Spider timeline; only one
690+-- value should exist for each type @x@
691+data SpiderTimelineEnv x = SpiderTimelineEnv
692+ { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
693+ , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
694+#ifdef DEBUG
695+ , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
696+#endif
697+ }
698+type role SpiderTimelineEnv nominal
699+
700+instance GEq SpiderTimelineEnv where
701+ a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
702+ then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
703+ else Nothing
704+
368data EventEnv x
705data EventEnv x
369− = EventEnv { eventEnvSpiderTimeline :: !(SpiderTimeline x)
370− , eventEnvAssignments :: !(IORef [SomeAssignment]) -- Needed for Subscribe
706+ = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
371− , eventEnvHoldInits :: !(IORef [SomeHoldInit]) -- Needed for Subscribe
707+ , eventEnvHoldInits :: !(IORef [SomeHoldInit x]) -- Needed for Subscribe
372− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate])
708+ , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
373− , eventEnvMergeInits :: !(IORef [SomeMergeInit]) -- Needed for Subscribe
709+ , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
374 , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
710 , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
375 , eventEnvRootClears :: !(IORef [SomeRootClear])
711 , eventEnvRootClears :: !(IORef [SomeRootClear])
376 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
712 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
377− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence]) -- Needed for Subscribe
713+ , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
378− , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
714+ , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
379 }
715 }
380
716
381{-# INLINE runEventM #-}
717{-# INLINE runEventM #-}
382−runEventM :: EventM x a -> EventEnv x -> IO a
718+runEventM :: EventM x a -> IO a
383−runEventM = runReaderT . unEventM
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)
384
723
385class MonadIO m => Defer a m where
724class MonadIO m => Defer a m where
386 getDeferralQueue :: m (IORef [a])
725 getDeferralQueue :: m (IORef [a])
@@ -391,91 +730,91 @@ defer a = do
391 q <- getDeferralQueue
730 q <- getDeferralQueue
392 liftIO $ modifyIORef' q (a:)
731 liftIO $ modifyIORef' q (a:)
393
732
394−instance Defer SomeAssignment (EventM x) where
733+instance HasSpiderTimeline x => Defer (SomeAssignment x) (EventM x) where
395 {-# INLINE getDeferralQueue #-}
734 {-# INLINE getDeferralQueue #-}
396− getDeferralQueue = EventM $ asks eventEnvAssignments
735+ getDeferralQueue = asksEventEnv eventEnvAssignments
397
736
398−instance Defer SomeHoldInit (EventM x) where
737+instance HasSpiderTimeline x => Defer (SomeHoldInit x) (EventM x) where
399 {-# INLINE getDeferralQueue #-}
738 {-# INLINE getDeferralQueue #-}
400− getDeferralQueue = EventM $ asks eventEnvHoldInits
739+ getDeferralQueue = asksEventEnv eventEnvHoldInits
401
740
402−instance Defer SomeHoldInit BehaviorM where
741+instance Defer (SomeHoldInit x) (BehaviorM x) where
403 {-# INLINE getDeferralQueue #-}
742 {-# INLINE getDeferralQueue #-}
404 getDeferralQueue = BehaviorM $ asks snd
743 getDeferralQueue = BehaviorM $ asks snd
405
744
406−instance Defer SomeMergeUpdate (EventM x) where
745+instance HasSpiderTimeline x => Defer (SomeMergeUpdate x) (EventM x) where
407 {-# INLINE getDeferralQueue #-}
746 {-# INLINE getDeferralQueue #-}
408− getDeferralQueue = EventM $ asks eventEnvMergeUpdates
747+ getDeferralQueue = asksEventEnv eventEnvMergeUpdates
409
748
410−instance Defer SomeMergeInit (EventM x) where
749+instance HasSpiderTimeline x => Defer (SomeMergeInit x) (EventM x) where
411 {-# INLINE getDeferralQueue #-}
750 {-# INLINE getDeferralQueue #-}
412− getDeferralQueue = EventM $ asks eventEnvMergeInits
751+ getDeferralQueue = asksEventEnv eventEnvMergeInits
413
752
414−class HasCurrentHeight m where
753+class HasSpiderTimeline x => HasCurrentHeight x m | m -> x where
415 getCurrentHeight :: m Height
754 getCurrentHeight :: m Height
416− scheduleMerge :: Height -> MergeSubscribed a -> m ()
755+ scheduleMerge :: Height -> EventM x () -> m ()
417
756
418−instance HasCurrentHeight (EventM x) where
757+instance HasSpiderTimeline x => HasCurrentHeight x (EventM x) where
419 {-# INLINE getCurrentHeight #-}
758 {-# INLINE getCurrentHeight #-}
420− getCurrentHeight = EventM $ do
759+ getCurrentHeight = do
421− heightRef <- asks eventEnvCurrentHeight
760+ heightRef <- asksEventEnv eventEnvCurrentHeight
422 liftIO $ readIORef heightRef
761 liftIO $ readIORef heightRef
423 {-# INLINE scheduleMerge #-}
762 {-# INLINE scheduleMerge #-}
424− scheduleMerge height subscribed = EventM $ do
763+ scheduleMerge height subscribed = do
425− delayedRef <- asks eventEnvDelayedMerges
764+ delayedRef <- asksEventEnv eventEnvDelayedMerges
426− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [DelayedMerge subscribed]
765+ liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [subscribed]
427−
428−class HasSpiderTimeline x m | m -> x where
429− -- | Retrieve the current SpiderTimeline
430− askSpiderTimeline :: m (SpiderTimeline x)
431
766
432−instance HasSpiderTimeline x (EventM x) where
767+#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
433− {-# INLINE askSpiderTimeline #-}
768+class x ~ Global => HasSpiderTimeline x where
434− askSpiderTimeline = EventM $ asks eventEnvSpiderTimeline
769+#else
770+class HasSpiderTimeline x where
771+#endif
772+ -- | Retrieve the current SpiderTimelineEnv
773+ spiderTimeline :: SpiderTimelineEnv x
435
774
436−instance HasSpiderTimeline x (SpiderHost x) where
775+instance HasSpiderTimeline Global where
437− {-# INLINE askSpiderTimeline #-}
776+ spiderTimeline = globalSpiderTimelineEnv
438− askSpiderTimeline = SpiderHost ask
439
777
440−putCurrentHeight :: Height -> EventM x ()
778+putCurrentHeight :: HasSpiderTimeline x => Height -> EventM x ()
441−putCurrentHeight h = EventM $ do
779+putCurrentHeight h = do
442− heightRef <- asks eventEnvCurrentHeight
780+ heightRef <- asksEventEnv eventEnvCurrentHeight
443 liftIO $ writeIORef heightRef $! h
781 liftIO $ writeIORef heightRef $! h
444
782
445−instance Defer SomeClear (EventM x) where
783+instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
446 {-# INLINE getDeferralQueue #-}
784 {-# INLINE getDeferralQueue #-}
447− getDeferralQueue = EventM $ asks eventEnvClears
785+ getDeferralQueue = asksEventEnv eventEnvClears
448
786
449{-# INLINE scheduleClear #-}
787{-# INLINE scheduleClear #-}
450scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
788scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
451scheduleClear r = defer $ SomeClear r
789scheduleClear r = defer $ SomeClear r
452
790
453−instance Defer SomeRootClear (EventM x) where
791+instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
454 {-# INLINE getDeferralQueue #-}
792 {-# INLINE getDeferralQueue #-}
455− getDeferralQueue = EventM $ asks eventEnvRootClears
793+ getDeferralQueue = asksEventEnv eventEnvRootClears
456
794
457{-# INLINE scheduleRootClear #-}
795{-# INLINE scheduleRootClear #-}
458scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
796scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
459scheduleRootClear r = defer $ SomeRootClear r
797scheduleRootClear r = defer $ SomeRootClear r
460
798
461−instance Defer SomeResetCoincidence (EventM x) where
799+instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
462 {-# INLINE getDeferralQueue #-}
800 {-# INLINE getDeferralQueue #-}
463− getDeferralQueue = EventM $ asks eventEnvResetCoincidences
801+ getDeferralQueue = asksEventEnv eventEnvResetCoincidences
464
802
465-- Note: hold cannot examine its event until after the phase is over
803-- 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) #-}
804+{- SPECIALIZE hold :: (HasSpiderTimeline x, Patch p) => PatchTarget p -> Event x p -> EventM x (Hold x p) #-}
467−{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> ComputeM (Hold p a) #-}
805+{-# INLINE [1] hold #-}
468−hold :: (R.Patch p, Defer SomeHoldInit m) => a -> Event (p a) -> m (Hold p a)
806+hold :: (Patch p, Defer (SomeHoldInit x) m) => PatchTarget p -> Event x p -> m (Hold x p)
469hold v0 e = do
807hold v0 e = do
470 valRef <- liftIO $ newIORef v0
808 valRef <- liftIO $ newIORef v0
471 invsRef <- liftIO $ newIORef []
809 invsRef <- liftIO $ newIORef []
472− parentRef <- liftIO $ newIORef $ Left e
810+ parentRef <- liftIO $ newIORef Nothing
473#ifdef DEBUG_NODEIDS
811#ifdef DEBUG_NODEIDS
474 nodeId <- liftIO newNodeId
812 nodeId <- liftIO newNodeId
475#endif
813#endif
476 let h = Hold
814 let h = Hold
477 { holdValue = valRef
815 { holdValue = valRef
478 , holdInvalidators = invsRef
816 , holdInvalidators = invsRef
817+ , holdEvent = e
479 , holdParent = parentRef
818 , holdParent = parentRef
480#ifdef DEBUG_NODEIDS
819#ifdef DEBUG_NODEIDS
481 , holdNodeId = nodeId
820 , holdNodeId = nodeId
@@ -485,37 +824,37 @@ hold v0 e = do
485 return h
824 return h
486
825
487{-# INLINE getHoldEventSubscription #-}
826{-# 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)
489getHoldEventSubscription h = do
828getHoldEventSubscription h = do
490 ep <- liftIO $ readIORef $ holdParent h
829 ep <- liftIO $ readIORef $ holdParent h
491 case ep of
830 case ep of
492− Right subd -> return subd
831+ Just subd -> return subd
493− Left e -> do
832+ Nothing -> do
833+ let e = holdEvent h
494 subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
834 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)
496 liftIO $ writeIORef subscriptionRef $! subscription
836 liftIO $ writeIORef subscriptionRef $! subscription
497− occ <- liftIO $ readEventSubscribed subd
498 case occ of
837 case occ of
499 Nothing -> return ()
838 Nothing -> return ()
500 Just o -> do
839 Just o -> do
501 old <- liftIO $ readIORef $ holdValue h
840 old <- liftIO $ readIORef $ holdValue h
502− case R.apply o old of
841+ case apply o old of
503 Nothing -> return ()
842 Nothing -> return ()
504 Just new -> do
843 Just new -> do
505 -- Need to evaluate these so that we don't retain the Hold itself
844 -- Need to evaluate these so that we don't retain the Hold itself
506 v <- liftIO $ evaluate $ holdValue h
845 v <- liftIO $ evaluate $ holdValue h
507 i <- liftIO $ evaluate $ holdInvalidators h
846 i <- liftIO $ evaluate $ holdInvalidators h
508 defer $ SomeAssignment v i new
847 defer $ SomeAssignment v i new
509− liftIO $ writeIORef (holdParent h) $ Right subscription
848+ liftIO $ writeIORef (holdParent h) $ Just subscription
510 return subscription
849 return subscription
511
850
512−type BehaviorEnv = (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]), IORef [SomeHoldInit])
851+type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
513
852
514--type role BehaviorM representational
853--type role BehaviorM representational
515-- BehaviorM can sample behaviors
854-- 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)
517
856
518−instance Monad BehaviorM where
857+instance Monad (BehaviorM x) where
519 {-# INLINE (>>=) #-}
858 {-# INLINE (>>=) #-}
520 BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
859 BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
521 {-# INLINE (>>) #-}
860 {-# INLINE (>>) #-}
@@ -525,94 +864,74 @@ instance Monad BehaviorM where
525 {-# INLINE fail #-}
864 {-# INLINE fail #-}
526 fail s = BehaviorM $ fail s
865 fail s = BehaviorM $ fail s
527
866
528−data BehaviorSubscribed a
867+data BehaviorSubscribed x a
529− = forall p. BehaviorSubscribedHold (Hold p a)
868+ = forall p. BehaviorSubscribedHold (Hold x p)
530− | BehaviorSubscribedPull (PullSubscribed a)
869+ | BehaviorSubscribedPull (PullSubscribed x a)
531
870
532−data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
871+data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
533
872
534--type role PullSubscribed representational
873--type role PullSubscribed representational
535−data PullSubscribed a
874+data PullSubscribed x a
536 = PullSubscribed { pullSubscribedValue :: !a
875 = PullSubscribed { pullSubscribedValue :: !a
537− , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
876+ , pullSubscribedInvalidators :: !(IORef [Weak (Invalidator x)])
538− , pullSubscribedOwnInvalidator :: !Invalidator
877+ , pullSubscribedOwnInvalidator :: !(Invalidator x)
539− , pullSubscribedParents :: ![SomeBehaviorSubscribed] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
878+ , pullSubscribedParents :: ![SomeBehaviorSubscribed x] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
540 }
879 }
541
880
542--type role Pull representational
881--type role Pull representational
543−data Pull a
882+data Pull x a
544− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
883+ = Pull { pullValue :: !(IORef (Maybe (PullSubscribed x a)))
545− , pullCompute :: !(BehaviorM a)
884+ , pullCompute :: !(BehaviorM x a)
546#ifdef DEBUG_NODEIDS
885#ifdef DEBUG_NODEIDS
547 , pullNodeId :: Int
886 , pullNodeId :: Int
548#endif
887#endif
549 }
888 }
550
889
551−data Invalidator
890+data Invalidator x
552− = forall a. InvalidatorPull (Pull a)
891+ = forall a. InvalidatorPull (Pull x a)
553− | forall a. InvalidatorSwitch (SwitchSubscribed a)
892+ | forall a. InvalidatorSwitch (SwitchSubscribed x a)
554
893
555−data RootSubscribed a = forall k. GCompare k => RootSubscribed
894+data RootSubscribed x a = forall k. GCompare k => RootSubscribed
556 { rootSubscribedKey :: !(k a)
895 { rootSubscribedKey :: !(k a)
557− , rootSubscribedCachedSubscribed :: !(IORef (DMap k RootSubscribed)) -- From the original Root
896+ , rootSubscribedCachedSubscribed :: !(IORef (DMap k (RootSubscribed x))) -- From the original Root
558− , rootSubscribedSubscribers :: !(SubscriberList a)
897+ , rootSubscribedSubscribers :: !(WeakBag (Subscriber x a))
559 , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
898 , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
560 , rootSubscribedUninit :: IO ()
899 , 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?
562#ifdef DEBUG_NODEIDS
901#ifdef DEBUG_NODEIDS
563 , rootSubscribedNodeId :: Int
902 , rootSubscribedNodeId :: Int
564#endif
903#endif
565 }
904 }
566
905
567−data Root (k :: * -> *)
906+data Root x (k :: * -> *)
568 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
907 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
569− , rootSubscribed :: !(IORef (DMap k RootSubscribed))
908+ , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
570− , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
909+ , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
571 }
910 }
572
911
573−data SomeHoldInit = forall p a. R.Patch p => SomeHoldInit !(Hold p a)
912+data SomeHoldInit x = forall p. Patch p => SomeHoldInit !(Hold x p)
574−
575−data SomeMergeUpdate = forall k. GCompare k => SomeMergeUpdate (R.PatchDMap (DMap k Event)) (MergeSubscribed k)
576
913
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+ }
578
919
579−data SomeEventSubscription = forall a. SomeEventSubscription (EventSubscription a)
920+newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
580
921
581-- EventM can do everything BehaviorM can, plus create holds
922-- 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
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
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− }
596
924
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)
598
926
599−instance Defer SomeHoldInit ComputeM where
927+instance Defer (SomeHoldInit x) (ComputeM x) where
600 getDeferralQueue = ComputeM ask
928 getDeferralQueue = ComputeM ask
601
929
602−{-# SPECIALIZE runComputeM :: ComputeM a -> EventM x a #-}
930+{-# INLINABLE runComputeM #-}
603−runComputeM :: Defer SomeHoldInit m => ComputeM a -> m a
931+runComputeM :: Defer (SomeHoldInit x) m => ComputeM x a -> m a
604runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
932runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
605
933
606−data Push a b
934+newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
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
616
935
617data HeightBag = HeightBag
936data HeightBag = HeightBag
618 { _heightBag_size :: {-# UNPACK #-} !Int
937 { _heightBag_size :: {-# UNPACK #-} !Int
@@ -656,252 +975,108 @@ heightBagVerify b@(HeightBag s c) = if
656heightBagVerify = id
975heightBagVerify = id
657#endif
976#endif
658
977
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
978+data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
681− { _fanSubscribedChildren_list :: !(SubscriberList a)
979+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
682− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed k)
980+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
683− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed k)))
981+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
684 }
982 }
685
983
686−data FanSubscribed k
984+data FanSubscribed (x :: *) k
687− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed k)))
985+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
986+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
688− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren k))) -- This DMap should never be empty
987+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
689− , fanSubscribedParent :: !(EventSubscription (DMap k Identity))
988+ , fanSubscribedParent :: !(EventSubscription x)
690#ifdef DEBUG_NODEIDS
989#ifdef DEBUG_NODEIDS
691 , fanSubscribedNodeId :: Int
990 , fanSubscribedNodeId :: Int
692#endif
991#endif
693 }
992 }
694
993
695−data Fan k
994+data Fan x k
696− = Fan { fanParent :: !(Event (DMap k Identity))
995+ = Fan { fanParent :: !(Event x (DMap k Identity))
697− , fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
996+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
698 }
997 }
699
998
700−data SwitchSubscribed a
999+data SwitchSubscribed x a
701− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
1000+ = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
702 , switchSubscribedOccurrence :: !(IORef (Maybe a))
1001 , switchSubscribedOccurrence :: !(IORef (Maybe a))
703 , switchSubscribedHeight :: !(IORef Height)
1002 , switchSubscribedHeight :: !(IORef Height)
704− , switchSubscribedSubscribers :: !(SubscriberList a)
1003+ , switchSubscribedSubscribers :: !(WeakBag (Subscriber x a))
705− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !Invalidator
1004+ , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !(Invalidator x)
706− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
1005+ , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak (Invalidator x)))
707− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
1006+ , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed x])
708− , switchSubscribedParent :: !(Behavior (Event a))
1007+ , switchSubscribedParent :: !(Behavior x (Event x a))
709− , switchSubscribedCurrentParent :: !(IORef (EventSubscription a))
1008+ , switchSubscribedCurrentParent :: !(IORef (EventSubscription x))
710− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed a)))
1009+ , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed x a)))
711#ifdef DEBUG_NODEIDS
1010#ifdef DEBUG_NODEIDS
712 , switchSubscribedNodeId :: Int
1011 , switchSubscribedNodeId :: Int
713#endif
1012#endif
714 }
1013 }
715
1014
716−data Switch a
1015+data Switch x a
717− = Switch { switchParent :: !(Behavior (Event a))
1016+ = Switch { switchParent :: !(Behavior x (Event x a))
718− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
1017+ , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
719 }
1018 }
720
1019
721{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
1020{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
722−data CoincidenceSubscribed a
1021+data CoincidenceSubscribed x a
723− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
1022+ = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
724 , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
1023 , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
725− , coincidenceSubscribedSubscribers :: !(SubscriberList a)
1024+ , coincidenceSubscribedSubscribers :: !(WeakBag (Subscriber x a))
726 , coincidenceSubscribedHeight :: !(IORef Height)
1025 , coincidenceSubscribedHeight :: !(IORef Height)
727− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
1026+ , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber x (Event x a))
728− , coincidenceSubscribedOuterParent :: !(EventSubscription (Event a))
1027+ , coincidenceSubscribedOuterParent :: !(EventSubscription x)
729− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
1028+ , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed x)))
730− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed a)))
1029+ , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed x a)))
731#ifdef DEBUG_NODEIDS
1030#ifdef DEBUG_NODEIDS
732 , coincidenceSubscribedNodeId :: Int
1031 , coincidenceSubscribedNodeId :: Int
733#endif
1032#endif
734 }
1033 }
735
1034
736−data Coincidence a
1035+data Coincidence x a
737− = Coincidence { coincidenceParent :: !(Event (Event a))
1036+ = Coincidence { coincidenceParent :: !(Event x (Event x a))
738− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
1037+ , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
739 }
1038 }
740
1039
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−
802{-# NOINLINE newInvalidatorSwitch #-}
1040{-# NOINLINE newInvalidatorSwitch #-}
803−newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
1041+newInvalidatorSwitch :: SwitchSubscribed x a -> IO (Invalidator x)
804newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1042newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
805
1043
806{-# NOINLINE newInvalidatorPull #-}
1044{-# NOINLINE newInvalidatorPull #-}
807−newInvalidatorPull :: Pull a -> IO Invalidator
1045+newInvalidatorPull :: Pull x a -> IO (Invalidator x)
808newInvalidatorPull p = return $! InvalidatorPull p
1046newInvalidatorPull p = return $! InvalidatorPull p
809
1047
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
849 fmapMaybe f = push $ return . f
1049 fmapMaybe f = push $ return . f
850
1050
851−instance Align Event where
1051+instance HasSpiderTimeline x => Align (Event x) where
852− nil = EventNever
1052+ nil = eventNever
853− align ea eb = R.fmapMaybe R.dmapToThese $ merge $ DynamicConst $ DMap.fromList [R.LeftTag :=> ea, R.RightTag :=> eb]
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)
854
1059
855--TODO: Avoid the duplication between this and R.zipDynWith
1060--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)
857zipDynWith f da db =
1062zipDynWith f da db =
858− let eab = align (updated da) (updated db)
1063+ let eab = align (dynamicUpdated da) (dynamicUpdated db)
859 ec = flip push eab $ \o -> do
1064 ec = flip push eab $ \o -> do
860 (a, b) <- case o of
1065 (a, b) <- case o of
861 This (Identity a) -> do
1066 This (Identity a) -> do
862− b <- readBehaviorUntracked $ current db
1067+ b <- readBehaviorUntracked $ dynamicCurrent db
863 return (a, b)
1068 return (a, b)
864 That (Identity b) -> do
1069 That (Identity b) -> do
865− a <- readBehaviorUntracked $ current da
1070+ a <- readBehaviorUntracked $ dynamicCurrent da
866 return (a, b)
1071 return (a, b)
867 These (Identity a) (Identity b) -> return (a, b)
1072 These (Identity a) (Identity b) -> return (a, b)
868 return $ Just $ Identity $ f a b
1073 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
870
1075
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
884unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
1077unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
885 where x = (readV0, v')
1078 where x = (readV0, v')
886
1079
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−
905-- ResultM can read behaviors and events
1080-- ResultM can read behaviors and events
906type ResultM = EventM
1081type ResultM = EventM
907
1082
@@ -909,24 +1084,19 @@ type ResultM = EventM
909unsafeNewIORef :: a -> b -> IORef b
1084unsafeNewIORef :: a -> b -> IORef b
910unsafeNewIORef _ b = unsafePerformIO $ newIORef b
1085unsafeNewIORef _ b = unsafePerformIO $ newIORef b
911
1086
912−instance Functor Event where
1087+instance HasSpiderTimeline x => Functor (Event x) where
913 fmap f = push $ return . Just . f
1088 fmap f = push $ return . Just . f
914
1089
915−instance Functor Behavior where
1090+instance Functor (Behavior x) where
916 fmap f = pull . fmap f . readBehaviorTracked
1091 fmap f = pull . fmap f . readBehaviorTracked
917
1092
918−{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
1093+{-# INLINE push #-}
919−push :: forall a b. (a -> ComputeM (Maybe b)) -> Event a -> Event b
1094+push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
920−push f e = EventPush $ Push
1095+push f e = cacheEvent (pushCheap f e)
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 #-}
926
1096
927−{-# NOINLINE pull #-}
1097+{-# INLINABLE pull #-}
928−pull :: BehaviorM a -> Behavior a
1098+pull :: BehaviorM x a -> Behavior x a
929−pull a = BehaviorPull $ Pull
1099+pull a = behaviorPull $ Pull
930 { pullCompute = a
1100 { pullCompute = a
931 , pullValue = unsafeNewIORef a Nothing
1101 , pullValue = unsafeNewIORef a Nothing
932#ifdef DEBUG_NODEIDS
1102#ifdef DEBUG_NODEIDS
@@ -934,29 +1104,25 @@ pull a = BehaviorPull $ Pull
934#endif
1104#endif
935 }
1105 }
936
1106
937−{-# NOINLINE switch #-}
1107+{-# INLINABLE switch #-}
938−switch :: Behavior (Event a) -> Event a
1108+switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
939−switch a = EventSwitch $ Switch
1109+switch a = eventSwitch $ Switch
940 { switchParent = a
1110 { switchParent = a
941 , switchSubscribed = unsafeNewIORef a Nothing
1111 , switchSubscribed = unsafeNewIORef a Nothing
942 }
1112 }
943
1113
944−coincidence :: Event (Event a) -> Event a
1114+coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
945−coincidence a = EventCoincidence $ Coincidence
1115+coincidence a = eventCoincidence $ Coincidence
946 { coincidenceParent = a
1116 { coincidenceParent = a
947 , coincidenceSubscribed = unsafeNewIORef a Nothing
1117 , coincidenceSubscribed = unsafeNewIORef a Nothing
948 }
1118 }
949
1119
950−propagateAndUpdateSubscribersRef :: SubscriberList a -> a -> EventM x ()
951−propagateAndUpdateSubscribersRef subscribersRef a = do
952− propagate a subscribersRef
953−
954-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1120-- 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
956run roots after = do
1122run roots after = do
957− tracePropagate $ "Running an event frame with " <> show (length roots) <> " events"
1123+ tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
958− spiderEnv <- SpiderHost ask
1124+ t <- SpiderHost ask
959− result <- SpiderHost $ lift $ withMVar (_spiderEnv_lock spiderEnv) $ \_ -> flip runReaderT spiderEnv $ unSpiderHost $ runFrame $ do
1125+ result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock t) $ \_ -> flip runReaderT t $ unSpiderHost $ runFrame $ do
960 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1126 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
961 occBefore <- liftIO $ do
1127 occBefore <- liftIO $ do
962 occBefore <- readIORef occRef
1128 occBefore <- readIORef occRef
@@ -967,43 +1133,38 @@ run roots after = do
967 return $ Just r
1133 return $ Just r
968 else return Nothing
1134 else return Nothing
969 forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
1135 forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
970− propagateAndUpdateSubscribersRef subscribersRef a
1136+ propagate a subscribersRef
971− delayedRef <- EventM $ asks eventEnvDelayedMerges
1137+ delayedRef <- asksEventEnv eventEnvDelayedMerges
972 let go = do
1138 let go = do
973 delayed <- liftIO $ readIORef delayedRef
1139 delayed <- liftIO $ readIORef delayedRef
974 case IntMap.minViewWithKey delayed of
1140 case IntMap.minViewWithKey delayed of
975 Nothing -> return ()
1141 Nothing -> return ()
976 Just ((currentHeight, cur), future) -> do
1142 Just ((currentHeight, cur), future) -> do
977− tracePropagate $ "Running height " ++ show currentHeight
1143+ tracePropagate (Proxy :: Proxy x) $ "Running height " ++ show currentHeight
978 putCurrentHeight $ Height currentHeight
1144 putCurrentHeight $ Height currentHeight
979 liftIO $ writeIORef delayedRef $! future
1145 liftIO $ writeIORef delayedRef $! future
980− forM_ cur $ \d -> case d of
1146+ sequence_ cur
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
993 go
1147 go
994 go
1148 go
995 putCurrentHeight maxBound
1149 putCurrentHeight maxBound
996 after
1150 after
997− tracePropagate "Done running an event frame"
1151+ tracePropagate (Proxy :: Proxy x) "Done running an event frame"
998 return result
1152 return result
999
1153
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+
1000data SomeClear = forall a. SomeClear (IORef (Maybe a))
1163data SomeClear = forall a. SomeClear (IORef (Maybe a))
1001
1164
1002data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
1165data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
1003
1166
1004−data SomeAssignment = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak Invalidator]) a
1167+data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1005−
1006−data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
1007
1168
1008debugFinalize :: Bool
1169debugFinalize :: Bool
1009debugFinalize = False
1170debugFinalize = False
@@ -1022,41 +1183,37 @@ type WeakList a = [Weak a]
1022#ifdef DEBUG
1183#ifdef DEBUG
1023withIncreasedDepth :: CanTrace x m => m a -> m a
1184withIncreasedDepth :: CanTrace x m => m a -> m a
1024withIncreasedDepth a = do
1185withIncreasedDepth a = do
1025− spiderEnv <- askSpiderTimeline
1186+ spiderTimeline <- askSpiderTimelineEnv
1026− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) succ
1187+ liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1027 result <- a
1188 result <- a
1028− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) pred
1189+ liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1029 return result
1190 return result
1030#else
1191#else
1031withIncreasedDepth :: m a -> m a
1192withIncreasedDepth :: m a -> m a
1032withIncreasedDepth = id
1193withIncreasedDepth = id
1033#endif
1194#endif
1034
1195
1035−type CanTrace x m = (HasSpiderTimeline x m, MonadIO m)
1196+type CanTrace x m = (HasSpiderTimeline x, MonadIO m)
1036
1197
1037{-# INLINE tracePropagate #-}
1198{-# INLINE tracePropagate #-}
1038−tracePropagate :: (CanTrace x m) => String -> m ()
1199+tracePropagate :: (CanTrace x m) => proxy x -> String -> m ()
1039−tracePropagate = traceWhen debugPropagate
1200+tracePropagate p = traceWhen p debugPropagate
1040−
1041−{-# INLINE traceSubscribe #-}
1042−traceSubscribe :: String -> IO ()
1043−traceSubscribe = when debugSubscribe . liftIO . putStrLn
1044
1201
1045{-# INLINE traceInvalidate #-}
1202{-# INLINE traceInvalidate #-}
1046traceInvalidate :: String -> IO ()
1203traceInvalidate :: String -> IO ()
1047traceInvalidate = when debugInvalidate . liftIO . putStrLn
1204traceInvalidate = when debugInvalidate . liftIO . putStrLn
1048
1205
1049{-# INLINE traceWhen #-}
1206{-# INLINE traceWhen #-}
1050−traceWhen :: (CanTrace x m) => Bool -> String -> m ()
1207+traceWhen :: (CanTrace x m) => proxy x -> Bool -> String -> m ()
1051−traceWhen b message = traceMWhen b $ return message
1208+traceWhen p b message = traceMWhen p b $ return message
1052
1209
1053{-# INLINE traceMWhen #-}
1210{-# INLINE traceMWhen #-}
1054−traceMWhen :: (CanTrace x m) => Bool -> m String -> m ()
1211+traceMWhen :: (CanTrace x m) => proxy x -> Bool -> m String -> m ()
1055−traceMWhen b getMessage = when b $ do
1212+traceMWhen _ b getMessage = when b $ do
1056 message <- getMessage
1213 message <- getMessage
1057#ifdef DEBUG
1214#ifdef DEBUG
1058− spiderEnv <- askSpiderTimeline
1215+ spiderTimeline <- askSpiderTimelineEnv
1059− d <- liftIO $ readIORef $ _spiderEnv_depth spiderEnv
1216+ d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1060#else
1217#else
1061 let d = 0
1218 let d = 0
1062#endif
1219#endif
@@ -1065,6 +1222,7 @@ traceMWhen b getMessage = when b $ do
1065whoCreatedIORef :: IORef a -> IO [String]
1222whoCreatedIORef :: IORef a -> IO [String]
1066whoCreatedIORef (IORef a) = whoCreated $! a
1223whoCreatedIORef (IORef a) = whoCreated $! a
1067
1224
1225+#ifdef DEBUG_CYCLES
1068groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
1226groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
1069groupByHead = \case
1227groupByHead = \case
1070 [] -> []
1228 [] -> []
@@ -1076,181 +1234,49 @@ groupByHead = \case
1076
1234
1077listsToForest :: Eq a => [[a]] -> Forest a
1235listsToForest :: Eq a => [[a]] -> Forest a
1078listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1236listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1079−
1237+#endif
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"
1164
1238
1165{-# INLINE propagateSubscriberHold #-}
1239{-# 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 ()
1167propagateSubscriberHold h a = do
1241propagateSubscriberHold h a = do
1168− {-# SCC "trace" #-} traceMWhen debugPropagate $ liftIO $ do
1242+ {-# SCC "trace" #-} traceMWhen (Proxy :: Proxy x) debugPropagate $ liftIO $ do
1169 invalidators <- liftIO $ readIORef $ holdInvalidators h
1243 invalidators <- liftIO $ readIORef $ holdInvalidators h
1170 return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1244 return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1171 v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1245 v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1172− case {-# SCC "apply" #-} R.apply a v of
1246+ case {-# SCC "apply" #-} apply a v of
1173 Nothing -> return ()
1247 Nothing -> return ()
1174 Just v' -> do
1248 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
1176 vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1250 vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1177 iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1251 iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1178 defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1252 defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1179
1253
1180−data SomeResetCoincidence = forall a. SomeResetCoincidence !(EventSubscription a) !(Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
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
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
1188
1255
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
1190runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1257runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1191
1258
1192−askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
1259+askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1193askInvalidator = do
1260askInvalidator = do
1194 (!m, _) <- BehaviorM ask
1261 (!m, _) <- BehaviorM ask
1195 case m of
1262 case m of
1196 Nothing -> return Nothing
1263 Nothing -> return Nothing
1197 Just (!wi, _) -> return $ Just wi
1264 Just (!wi, _) -> return $ Just wi
1198
1265
1199−askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
1266+askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1200askParentsRef = do
1267askParentsRef = do
1201 (!m, _) <- BehaviorM ask
1268 (!m, _) <- BehaviorM ask
1202 case m of
1269 case m of
1203 Nothing -> return Nothing
1270 Nothing -> return Nothing
1204 Just (_, !p) -> return $ Just p
1271 Just (_, !p) -> return $ Just p
1205
1272
1206−askBehaviorHoldInits :: BehaviorM (IORef [SomeHoldInit])
1273+askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1207askBehaviorHoldInits = do
1274askBehaviorHoldInits = do
1208 (_, !his) <- BehaviorM ask
1275 (_, !his) <- BehaviorM ask
1209 return his
1276 return his
1210
1277
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−
1252{-# INLINE getDynHold #-}
1278{-# 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)
1254getDynHold d = do
1280getDynHold d = do
1255 mh <- liftIO $ readIORef $ unDyn d
1281 mh <- liftIO $ readIORef $ unDyn d
1256 case mh of
1282 case mh of
@@ -1262,72 +1288,30 @@ getDynHold d = do
1262 liftIO $ writeIORef (unDyn d) $ Right h
1288 liftIO $ writeIORef (unDyn d) $ Right h
1263 return h
1289 return h
1264
1290
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−
1298-- Always refers to 0
1291-- Always refers to 0
1299{-# NOINLINE zeroRef #-}
1292{-# NOINLINE zeroRef #-}
1300zeroRef :: IORef Height
1293zeroRef :: IORef Height
1301zeroRef = unsafePerformIO $ newIORef zeroHeight
1294zeroRef = unsafePerformIO $ newIORef zeroHeight
1302
1295
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−
1315type CanSubscribe x m =
1296type CanSubscribe x m =
1316− ( HasSpiderTimeline x m
1297+ ( HasSpiderTimeline x
1317− , HasCurrentHeight m
1298+ , HasCurrentHeight x m
1318− , Defer SomeHoldInit m, Defer SomeMergeInit m -- The things we need to lazily initialize state
1299+ , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
1319 , Defer SomeClear m -- Wiping out event firings
1300 , Defer SomeClear m -- Wiping out event firings
1320− , Defer SomeAssignment m -- Updating state
1301+ , Defer (SomeAssignment x) m -- Updating state
1321− , Defer SomeResetCoincidence m
1302+ , Defer (SomeResetCoincidence x) m
1303+ , m ~ EventM x
1322 )
1304 )
1323
1305
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)
1325getRootSubscribed k r sub = do
1307getRootSubscribed k r sub = do
1326 mSubscribed <- readIORef $ rootSubscribed r
1308 mSubscribed <- readIORef $ rootSubscribed r
1309+ let getOcc = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1327 case DMap.lookup k mSubscribed of
1310 case DMap.lookup k mSubscribed of
1328 Just subscribed -> {-# SCC "hitRoot" #-} do
1311 Just subscribed -> {-# SCC "hitRoot" #-} do
1329 sln <- subscribeRootSubscribed subscribed sub
1312 sln <- subscribeRootSubscribed subscribed sub
1313+ occ <- getOcc
1330− return (sln, subscribed)
1314+ return (sln, subscribed, occ)
1331 Nothing -> {-# SCC "missRoot" #-} do
1315 Nothing -> {-# SCC "missRoot" #-} do
1332 weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1316 weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1333 let !cached = rootSubscribed r
1317 let !cached = rootSubscribed r
@@ -1339,7 +1323,7 @@ getRootSubscribed k r sub = do
1339 let subscribed = RootSubscribed
1323 let subscribed = RootSubscribed
1340 { rootSubscribedKey = k
1324 { rootSubscribedKey = k
1341 , rootSubscribedCachedSubscribed = cached
1325 , rootSubscribedCachedSubscribed = cached
1342− , rootSubscribedOccurrence = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1326+ , rootSubscribedOccurrence = getOcc
1343 , rootSubscribedSubscribers = subs
1327 , rootSubscribedSubscribers = subs
1344 , rootSubscribedUninit = uninit
1328 , rootSubscribedUninit = uninit
1345 , rootSubscribedWeakSelf = weakSelf
1329 , rootSubscribedWeakSelf = weakSelf
@@ -1349,153 +1333,41 @@ getRootSubscribed k r sub = do
1349 }
1333 }
1350 writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "RootSubscribed"
1334 writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "RootSubscribed"
1351 modifyIORef' (rootSubscribed r) $ DMap.insertWith (error $ "getRootSubscribed: duplicate key inserted into Root") k subscribed --TODO: I think we can just write back mSubscribed rather than re-reading it
1335 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
1336+ occ <- getOcc
1352− return (sln, subscribed)
1337+ return (sln, subscribed, occ)
1353
1338
1354{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1339{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1355−cleanupRootSubscribed :: RootSubscribed a -> IO ()
1340+cleanupRootSubscribed :: RootSubscribed x a -> IO ()
1356cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1341cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1357 rootSubscribedUninit self
1342 rootSubscribedUninit self
1358 modifyIORef' cached $ DMap.delete k
1343 modifyIORef' cached $ DMap.delete k
1359
1344
1360{-# INLINE subscribeRootSubscribed #-}
1345{-# INLINE subscribeRootSubscribed #-}
1361−subscribeRootSubscribed :: RootSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1346+subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1362subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1347subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1363
1348
1364−-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
1349+{-# INLINABLE getFanSubscribed #-}
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)
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)
1483getFanSubscribed k f sub = do
1351getFanSubscribed k f sub = do
1484 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1352 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1485 case mSubscribed of
1353 case mSubscribed of
1486− Just subscribed -> {-# SCC "hitFan" #-} do
1354+ Just subscribed -> {-# SCC "hitFan" #-} liftIO $ do
1487− sln <- liftIO $ subscribeFanSubscribed k subscribed sub
1355+ sln <- subscribeFanSubscribed k subscribed sub
1356+ occ <- readIORef $ fanSubscribedOccurrence subscribed
1488− return (sln, subscribed)
1357+ return (sln, subscribed, coerce $ DMap.lookup k =<< occ)
1489 Nothing -> {-# SCC "missFan" #-} do
1358 Nothing -> {-# SCC "missFan" #-} do
1490 subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1359 subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1491 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1360 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1492 s <- liftIO $ newSubscriberFan subscribedUnsafe
1361 s <- liftIO $ newSubscriberFan subscribedUnsafe
1493− subscription <- subscribe (fanParent f) s
1362+ (subscription, parentOcc) <- subscribeAndRead (fanParent f) s
1494 weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1363 weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1495 (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1364 (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1496 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1365 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1366+ occRef <- liftIO $ newIORef parentOcc
1367+ when (isJust parentOcc) $ scheduleClear occRef
1497 let subscribed = FanSubscribed
1368 let subscribed = FanSubscribed
1498 { fanSubscribedCachedSubscribed = fanSubscribed f
1369 { fanSubscribedCachedSubscribed = fanSubscribed f
1370+ , fanSubscribedOccurrence = occRef
1499 , fanSubscribedParent = subscription
1371 , fanSubscribedParent = subscription
1500 , fanSubscribedSubscribers = subscribersRef
1372 , fanSubscribedSubscribers = subscribersRef
1501#ifdef DEBUG_NODEIDS
1373#ifdef DEBUG_NODEIDS
@@ -1507,9 +1379,9 @@ getFanSubscribed k f sub = do
1507 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1379 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1508 liftIO $ writeIORef subscribedRef $! subscribed
1380 liftIO $ writeIORef subscribedRef $! subscribed
1509 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1381 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1510− return (slnForSub, subscribed)
1382+ return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1511
1383
1512−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed k) -> IO ()
1384+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1513cleanupFanSubscribed (k, subscribed) = do
1385cleanupFanSubscribed (k, subscribed) = do
1514 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1386 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1515 let reducedSubscribers = DMap.delete k subscribers
1387 let reducedSubscribers = DMap.delete k subscribers
@@ -1521,7 +1393,7 @@ cleanupFanSubscribed (k, subscribed) = do
1521 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1393 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1522
1394
1523{-# INLINE subscribeFanSubscribed #-}
1395{-# 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
1525subscribeFanSubscribed k subscribed sub = do
1397subscribeFanSubscribed k subscribed sub = do
1526 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1398 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1527 case DMap.lookup k subscribers of
1399 case DMap.lookup k subscribers of
@@ -1529,18 +1401,19 @@ subscribeFanSubscribed k subscribed sub = do
1529 let !self = (k, subscribed)
1401 let !self = (k, subscribed)
1530 weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1402 weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1531 (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1403 (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
1533 return sln
1405 return sln
1534 Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1406 Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1535
1407
1536−{-# SPECIALIZE getSwitchSubscribed :: Switch a -> Subscriber a -> EventM x (SubscriberListTicket a, SwitchSubscribed a) #-}
1408+{-# INLINABLE getSwitchSubscribed #-}
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)
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)
1538getSwitchSubscribed s sub = do
1410getSwitchSubscribed s sub = do
1539 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1411 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1540 case mSubscribed of
1412 case mSubscribed of
1541− Just subscribed -> {-# SCC "hitSwitch" #-} do
1413+ Just subscribed -> {-# SCC "hitSwitch" #-} liftIO $ do
1542− sln <- liftIO $ subscribeSwitchSubscribed subscribed sub
1414+ sln <- subscribeSwitchSubscribed subscribed sub
1415+ occ <- readIORef $ switchSubscribedOccurrence subscribed
1543− return (sln, subscribed)
1416+ return (sln, subscribed, occ)
1544 Nothing -> {-# SCC "missSwitch" #-} do
1417 Nothing -> {-# SCC "missSwitch" #-} do
1545 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1418 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1546 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1419 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
@@ -1551,12 +1424,11 @@ getSwitchSubscribed s sub = do
1551 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1424 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1552 holdInits <- getDeferralQueue
1425 holdInits <- getDeferralQueue
1553 e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1426 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
1555 subscriptionRef <- liftIO $ newIORef subscription
1429 subscriptionRef <- liftIO $ newIORef subscription
1556− parentOcc <- liftIO $ readEventSubscribed subd
1557 occRef <- liftIO $ newIORef parentOcc
1430 occRef <- liftIO $ newIORef parentOcc
1558 when (isJust parentOcc) $ scheduleClear occRef
1431 when (isJust parentOcc) $ scheduleClear occRef
1559− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
1560 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1432 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1561 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1433 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1562 let !subscribed = SwitchSubscribed
1434 let !subscribed = SwitchSubscribed
@@ -1577,32 +1449,32 @@ getSwitchSubscribed s sub = do
1577 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1449 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1578 liftIO $ writeIORef subscribedRef $! subscribed
1450 liftIO $ writeIORef subscribedRef $! subscribed
1579 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1451 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1580− return (slnForSub, subscribed)
1452+ return (slnForSub, subscribed, parentOcc)
1581
1453
1582−cleanupSwitchSubscribed :: SwitchSubscribed a -> IO ()
1454+cleanupSwitchSubscribed :: SwitchSubscribed x a -> IO ()
1583cleanupSwitchSubscribed subscribed = do
1455cleanupSwitchSubscribed subscribed = do
1584 unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1456 unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1585 finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1457 finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1586 writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1458 writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1587
1459
1588{-# INLINE subscribeSwitchSubscribed #-}
1460{-# INLINE subscribeSwitchSubscribed #-}
1589−subscribeSwitchSubscribed :: SwitchSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1461+subscribeSwitchSubscribed :: SwitchSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1590subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1462subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1591
1463
1592−{-# SPECIALIZE getCoincidenceSubscribed :: Coincidence a -> Subscriber a -> EventM x (SubscriberListTicket a, CoincidenceSubscribed a) #-}
1464+{-# INLINABLE getCoincidenceSubscribed #-}
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)
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)
1594getCoincidenceSubscribed c sub = do
1466getCoincidenceSubscribed c sub = do
1595 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1467 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1596 case mSubscribed of
1468 case mSubscribed of
1597− Just subscribed -> {-# SCC "hitCoincidence" #-} do
1469+ Just subscribed -> {-# SCC "hitCoincidence" #-} liftIO $ do
1598− sln <- liftIO $ subscribeCoincidenceSubscribed subscribed sub
1470+ sln <- subscribeCoincidenceSubscribed subscribed sub
1471+ occ <- readIORef $ coincidenceSubscribedOccurrence subscribed
1599− return (sln, subscribed)
1472+ return (sln, subscribed, occ)
1600 Nothing -> {-# SCC "missCoincidence" #-} do
1473 Nothing -> {-# SCC "missCoincidence" #-} do
1601 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1474 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1602 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1475 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1603 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1476 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1604− outerSubscription@(EventSubscription _ outerSubd) <- subscribe (coincidenceParent c) subOuter
1477+ (outerSubscription@(EventSubscription _ outerSubd), outerOcc) <- subscribeAndRead (coincidenceParent c) subOuter
1605− outerOcc <- liftIO $ readEventSubscribed outerSubd
1606 outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1478 outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1607 (occ, height, mInnerSubd) <- case outerOcc of
1479 (occ, height, mInnerSubd) <- case outerOcc of
1608 Nothing -> return (Nothing, outerHeight, Nothing)
1480 Nothing -> return (Nothing, outerHeight, Nothing)
@@ -1632,35 +1504,158 @@ getCoincidenceSubscribed c sub = do
1632 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1504 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1633 liftIO $ writeIORef subscribedRef $! subscribed
1505 liftIO $ writeIORef subscribedRef $! subscribed
1634 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1506 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1635− return (slnForSub, subscribed)
1507+ return (slnForSub, subscribed, occ)
1636
1508
1637−cleanupCoincidenceSubscribed :: CoincidenceSubscribed a -> IO ()
1509+cleanupCoincidenceSubscribed :: CoincidenceSubscribed x a -> IO ()
1638cleanupCoincidenceSubscribed subscribed = do
1510cleanupCoincidenceSubscribed subscribed = do
1639 unsubscribe $ coincidenceSubscribedOuterParent subscribed
1511 unsubscribe $ coincidenceSubscribedOuterParent subscribed
1640 writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1512 writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1641
1513
1642{-# INLINE subscribeCoincidenceSubscribed #-}
1514{-# INLINE subscribeCoincidenceSubscribed #-}
1643−subscribeCoincidenceSubscribed :: CoincidenceSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1515+subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1644subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1516subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1645
1517
1646{-# INLINE merge #-}
1518{-# INLINE merge #-}
1647−merge :: GCompare k => Dynamic R.PatchDMap (DMap k Event) -> Event (DMap k Identity)
1519+merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1648−merge m = EventMerge $ Merge
1520+merge d = cacheEvent (mergeCheap d)
1649− { mergeParents = m
1521+
1650− , mergeSubscribed = unsafeNewIORef m Nothing
1522+{-# INLINE [1] mergeCheap #-}
1651− }
1523+mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1652−
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+
1653−newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
1648+newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
1654−
1649+
1655−fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
1650+fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
1656fan e =
1651fan e =
1657 let f = Fan
1652 let f = Fan
1658 { fanParent = e
1653 { fanParent = e
1659 , fanSubscribed = unsafeNewIORef e Nothing
1654 , fanSubscribed = unsafeNewIORef e Nothing
1660 }
1655 }
1661− in EventSelector $ \k -> EventFan k f
1656+ in EventSelector $ \k -> eventFan k f
1662
1657
1663−runHoldInits :: IORef [SomeHoldInit] -> IORef [SomeMergeInit] -> EventM x ()
1658+runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeMergeInit x] -> EventM x ()
1664runHoldInits holdInitRef mergeInitRef = do
1659runHoldInits holdInitRef mergeInitRef = do
1665 holdInits <- liftIO $ readIORef holdInitRef
1660 holdInits <- liftIO $ readIORef holdInitRef
1666 mergeInits <- liftIO $ readIORef mergeInitRef
1661 mergeInits <- liftIO $ readIORef mergeInitRef
@@ -1668,26 +1663,14 @@ runHoldInits holdInitRef mergeInitRef = do
1668 liftIO $ writeIORef holdInitRef []
1663 liftIO $ writeIORef holdInitRef []
1669 liftIO $ writeIORef mergeInitRef []
1664 liftIO $ writeIORef mergeInitRef []
1670 mapM_ initHold holdInits
1665 mapM_ initHold holdInits
1671− mapM_ initMerge mergeInits
1666+ mapM_ unSomeMergeInit mergeInits
1672 runHoldInits holdInitRef mergeInitRef
1667 runHoldInits holdInitRef mergeInitRef
1673
1668
1674−initHold :: SomeHoldInit -> EventM x ()
1669+initHold :: HasSpiderTimeline x => SomeHoldInit x -> EventM x ()
1675initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1670initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1676
1671
1677−initMerge :: SomeMergeInit -> EventM x ()
1672+newEventEnv :: IO (EventEnv x)
1678−initMerge (SomeMergeInit subscribed changed) = do
1673+newEventEnv = 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
1691 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1674 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1692 holdInitRef <- newIORef []
1675 holdInitRef <- newIORef []
1693 mergeUpdateRef <- newIORef []
1676 mergeUpdateRef <- newIORef []
@@ -1696,50 +1679,48 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1696 toClearRef <- newIORef []
1679 toClearRef <- newIORef []
1697 toClearRootRef <- newIORef []
1680 toClearRootRef <- newIORef []
1698 coincidenceInfosRef <- newIORef []
1681 coincidenceInfosRef <- newIORef []
1699− delayedRef <- liftIO $ newIORef IntMap.empty
1682+ delayedRef <- newIORef IntMap.empty
1700− let env = EventEnv spiderEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
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)
1701 let go = do
1701 let go = do
1702 result <- a
1702 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
1704 return result
1704 return result
1705− result <- runEventM go env
1705+ result <- runEventM go
1706− toClear <- readIORef toClearRef
1706+ toClear <- readIORef $ eventEnvClears env
1707 forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1707 forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1708− toClearRoot <- readIORef toClearRootRef
1708+ toClearRoot <- readIORef $ eventEnvRootClears env
1709 forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1709 forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1710− toAssign <- readIORef toAssignRef
1710+ toAssign <- readIORef $ eventEnvAssignments env
1711 toReconnectRef <- newIORef []
1711 toReconnectRef <- newIORef []
1712− coincidenceInfos <- readIORef coincidenceInfosRef
1712+ coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
1713 forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1713 forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1714 writeIORef vRef v
1714 writeIORef vRef v
1715 when debugInvalidate $ putStrLn $ "Invalidating Hold"
1715 when debugInvalidate $ putStrLn $ "Invalidating Hold"
1716 writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1716 writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1717− mergeUpdates <- readIORef mergeUpdateRef
1717+ mergeUpdates <- readIORef $ eventEnvMergeUpdates env
1718− writeIORef mergeUpdateRef []
1718+ writeIORef (eventEnvMergeUpdates env) []
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
1739 when debugPropagate $ putStrLn "Updating merges"
1719 when debugPropagate $ putStrLn "Updating merges"
1740− mergeSubscriptionsToKill <- concat <$> mapM updateMerge mergeUpdates
1720+ mergeSubscriptionsToKill <- runEventM $ concat <$> mapM _someMergeUpdate_update mergeUpdates
1741 when debugPropagate $ putStrLn "Updating merges done"
1721 when debugPropagate $ putStrLn "Updating merges done"
1742 toReconnect <- readIORef toReconnectRef
1722 toReconnect <- readIORef toReconnectRef
1723+ clearEventEnv env
1743 switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1724 switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1744 oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1725 oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1745 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1726 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
@@ -1749,12 +1730,12 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1749 wi' <- mkWeakPtrWithDebug i "wi'"
1730 wi' <- mkWeakPtrWithDebug i "wi'"
1750 writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1731 writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1751 writeIORef (switchSubscribedBehaviorParents subscribed) []
1732 writeIORef (switchSubscribedBehaviorParents subscribed) []
1752− writeIORef holdInitRef [] --TODO: Should we reuse this?
1733+ writeIORef (eventEnvHoldInits env) [] --TODO: Should we reuse this?
1753− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) holdInitRef
1734+ e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) $ eventEnvHoldInits env
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
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
1755 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1736 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1756 sub <- newSubscriberSwitch subscribed
1737 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
1758 {-
1739 {-
1759 stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1740 stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1760 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1741 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
@@ -1763,9 +1744,9 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1763 _ -> "something else"
1744 _ -> "something else"
1764 -}
1745 -}
1765 writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1746 writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1766− return $ SomeEventSubscription oldSubscription
1747+ return oldSubscription
1767− forM_ mergeSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1748+ liftIO $ mapM_ unsubscribe mergeSubscriptionsToKill
1768− forM_ switchSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1749+ liftIO $ mapM_ unsubscribe switchSubscriptionsToKill
1769 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1750 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1770 EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1751 EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1771 parentHeight <- getEventSubscribedHeight subd'
1752 parentHeight <- getEventSubscribedHeight subd'
@@ -1773,13 +1754,12 @@ runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1773 when (parentHeight /= myHeight) $ do
1754 when (parentHeight /= myHeight) $ do
1774 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1755 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1775 WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1756 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
1778 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1758 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1779 unsubscribe subscription
1759 unsubscribe subscription
1780 mapM_ invalidateCoincidenceHeight mcs
1760 mapM_ invalidateCoincidenceHeight mcs
1781 forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1761 forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1782− forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> revalidateMergeHeight m
1762+ mapM_ _someMergeUpdate_recalculateHeight mergeUpdates
1783 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1763 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1784 height <- calculateSwitchHeight subscribed
1764 height <- calculateSwitchHeight subscribed
1785 updateSwitchHeight height subscribed
1765 updateSwitchHeight height subscribed
@@ -1795,10 +1775,12 @@ zeroHeight = Height 0
1795invalidHeight :: Height
1775invalidHeight :: Height
1796invalidHeight = Height (-1000)
1776invalidHeight = Height (-1000)
1797
1777
1778+#ifdef DEBUG_CYCLES
1798-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1779-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1799{-# INLINE invalidHeightBeingTraversed #-}
1780{-# INLINE invalidHeightBeingTraversed #-}
1800invalidHeightBeingTraversed :: Height
1781invalidHeightBeingTraversed :: Height
1801invalidHeightBeingTraversed = Height (-1001)
1782invalidHeightBeingTraversed = Height (-1001)
1783+#endif
1802
1784
1803{-# INLINE succHeight #-}
1785{-# INLINE succHeight #-}
1804succHeight :: Height -> Height
1786succHeight :: Height -> Height
@@ -1807,121 +1789,14 @@ succHeight h@(Height a) =
1807 then invalidHeight
1789 then invalidHeight
1808 else Height $ succ a
1790 else Height $ succ a
1809
1791
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 ()
1885invalidateCoincidenceHeight subscribed = do
1793invalidateCoincidenceHeight subscribed = do
1886 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1794 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1887 when (oldHeight /= invalidHeight) $ do
1795 when (oldHeight /= invalidHeight) $ do
1888 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1796 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1889 WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1797 WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1890
1798
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 ()
1925updateSwitchHeight new subscribed = do
1800updateSwitchHeight new subscribed = do
1926 oldHeight <- readIORef $ switchSubscribedHeight subscribed
1801 oldHeight <- readIORef $ switchSubscribedHeight subscribed
1927 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1802 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
@@ -1929,7 +1804,7 @@ updateSwitchHeight new subscribed = do
1929 writeIORef (switchSubscribedHeight subscribed) $! new
1804 writeIORef (switchSubscribedHeight subscribed) $! new
1930 WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1805 WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1931
1806
1932−recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1807+recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1933recalculateCoincidenceHeight subscribed = do
1808recalculateCoincidenceHeight subscribed = do
1934 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1809 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1935 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1810 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
@@ -1938,18 +1813,18 @@ recalculateCoincidenceHeight subscribed = do
1938 writeIORef (coincidenceSubscribedHeight subscribed) $! height
1813 writeIORef (coincidenceSubscribedHeight subscribed) $! height
1939 WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1814 WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1940
1815
1941−calculateSwitchHeight :: SwitchSubscribed a -> IO Height
1816+calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
1942calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1817calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1943
1818
1944−calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Height
1819+calculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO Height
1945calculateCoincidenceHeight subscribed = do
1820calculateCoincidenceHeight subscribed = do
1946 outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1821 outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1947 innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1822 innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1948 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1823 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1949
1824
1950−data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed a)
1825+data SomeSwitchSubscribed x = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed x a)
1951
1826
1952−invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1827+invalidate :: IORef [SomeSwitchSubscribed x] -> WeakList (Invalidator x) -> IO (WeakList (Invalidator x))
1953invalidate toReconnectRef wis = do
1828invalidate toReconnectRef wis = do
1954 forM_ wis $ \wi -> do
1829 forM_ wis $ \wi -> do
1955 mi <- deRefWeak wi
1830 mi <- deRefWeak wi
@@ -1975,174 +1850,95 @@ invalidate toReconnectRef wis = do
1975-- Reflex integration
1850-- Reflex integration
1976--------------------------------------------------------------------------------
1851--------------------------------------------------------------------------------
1977
1852
1978−-- | The default, global Spider environment
1853+#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1979−type Spider = SpiderTimeline Global
1854+-- When we're specializing, we include this from Reflex.Class instead, because
1980−
1855+-- it needs access to these types; when we're not, we want them here, so our
1981−-- | Designates the default, global Spider timeline
1856+-- instance isn't an orphan instance.
1982−data Global
1857+#include "SpiderTimeline.include.hs"
1983−
1858+#include "SpiderTimelineHost.include.hs"
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)
1996#endif
1859#endif
1997− }
1998−type role SpiderTimeline nominal
1999
1860
2000−instance GEq SpiderTimeline where
1861+unsafeNewSpiderTimelineEnv :: forall x. IO (SpiderTimelineEnv x)
2001− a `geq` b = if _spiderEnv_toUnsubscribe a == _spiderEnv_toUnsubscribe b
1862+unsafeNewSpiderTimelineEnv = do
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 []
2008 lock <- newMVar ()
1863 lock <- newMVar ()
2009− cleanups <- newIORef []
1864+ env <- newEventEnv
2010#ifdef DEBUG
1865#ifdef DEBUG
2011 depthRef <- newIORef 0
1866 depthRef <- newIORef 0
2012#endif
1867#endif
2013− return $ SpiderTimeline
1868+ return $ SpiderTimelineEnv
2014− { _spiderEnv_toUnsubscribe = toUnsubscribe
1869+ { _spiderTimeline_lock = lock
2015− , _spiderEnv_lock = lock
1870+ , _spiderTimeline_eventEnv = env
2016− , _spiderEnv_cleanups = cleanups
2017#ifdef DEBUG
1871#ifdef DEBUG
2018− , _spiderEnv_depth = depthRef
1872+ , _spiderTimeline_depth = depthRef
2019#endif
1873#endif
2020 }
1874 }
2021
1875
2022−-- | Create a new SpiderTimeline
1876+-- | Create a new SpiderTimelineEnv
2023−newSpiderTimeline :: IO (Some SpiderTimeline)
1877+newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2024newSpiderTimeline = withSpiderTimeline (pure . Some.This)
1878newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2025
1879
2026-- | Pass a new timeline to the given function.
1880-- | Pass a new timeline to the given function.
2027−withSpiderTimeline :: (forall x. SpiderTimeline x -> IO r) -> IO r
1881+withSpiderTimeline :: (forall x. SpiderTimelineEnv x -> IO r) -> IO r
2028−withSpiderTimeline k = unsafeNewSpiderTimeline >>= k
1882+withSpiderTimeline k = unsafeNewSpiderTimelineEnv >>= k
2029
1883
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)
2031
1885
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)
2033
1887
1888+#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
2034−instance R.Reflex (SpiderTimeline x) where
1889+instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
1890+ {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2035− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior a }
1891+ newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2036− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event a }
1892+ newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2037− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic Identity a } deriving (Functor, Applicative, Monad)
1893+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2038− newtype Incremental (SpiderTimeline x) p a = SpiderIncremental { unSpiderIncremental :: Dynamic p a }
1894+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2039 type PullM (SpiderTimeline x) = SpiderPullM x
1895 type PullM (SpiderTimeline x) = SpiderPullM x
2040 type PushM (SpiderTimeline x) = SpiderPushM x
1896 type PushM (SpiderTimeline x) = SpiderPushM x
2041 {-# INLINABLE never #-}
1897 {-# INLINABLE never #-}
2042− never = SpiderEvent EventNever
1898+ never = SpiderEvent eventNever
2043 {-# INLINABLE constant #-}
1899 {-# INLINABLE constant #-}
2044− constant = SpiderBehavior . BehaviorConst
1900+ constant = SpiderBehavior . behaviorConst
2045− {-# INLINABLE push #-}
1901+ {-# INLINE push #-}
2046 push f = SpiderEvent . push (coerce f) . unSpiderEvent
1902 push f = SpiderEvent . push (coerce f) . unSpiderEvent
2047 {-# INLINABLE pull #-}
1903 {-# INLINABLE pull #-}
2048 pull = SpiderBehavior . pull . coerce
1904 pull = SpiderBehavior . pull . coerce
2049 {-# INLINABLE merge #-}
1905 {-# 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))
2051 {-# INLINABLE fan #-}
1907 {-# INLINABLE fan #-}
2052 fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
1908 fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2053 {-# INLINABLE switch #-}
1909 {-# 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
2055 {-# INLINABLE coincidence #-}
1911 {-# 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
2057 {-# INLINABLE current #-}
1913 {-# INLINABLE current #-}
2058− current = SpiderBehavior . current . unSpiderDynamic
1914+ current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2059 {-# INLINABLE updated #-}
1915 {-# INLINABLE updated #-}
2060− updated = SpiderEvent . fmap runIdentity . updated . unSpiderDynamic
1916+ updated = SpiderEvent . fmap runIdentity . dynamicUpdated . unSpiderDynamic
2061 {-# INLINABLE unsafeBuildDynamic #-}
1917 {-# 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'
2063 {-# INLINABLE unsafeBuildIncremental #-}
1919 {-# INLINABLE unsafeBuildIncremental #-}
2064− unsafeBuildIncremental readV0 dv = SpiderIncremental $ DynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
1920+ unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
2065 {-# INLINABLE mergeIncremental #-}
1921 {-# 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
2067 {-# INLINABLE currentIncremental #-}
1923 {-# INLINABLE currentIncremental #-}
2068− currentIncremental = SpiderBehavior . current . unSpiderIncremental
1924+ currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2069 {-# INLINABLE updatedIncremental #-}
1925 {-# INLINABLE updatedIncremental #-}
2070− updatedIncremental = SpiderEvent . updated . unSpiderIncremental
1926+ updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
2071 {-# INLINABLE incrementalToDynamic #-}
1927 {-# INLINABLE incrementalToDynamic #-}
2072− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ current i) $ flip push (updated i) $ \p -> do
1928+ incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip push (dynamicUpdated i) $ \p -> do
2073− c <- readBehaviorUntracked $ current i
1929+ c <- readBehaviorUntracked $ dynamicCurrent i
2074− return $ Identity <$> R.apply p c --TODO: Avoid the redundant 'apply'
1930+ return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2075−
1931+ eventCoercion Coercion = Coercion
2076−instance R.MonadSample (SpiderTimeline x) (EventM x) where
1932+ behaviorCoercion Coercion = Coercion
2077− {-# INLINABLE sample #-}
1933+ dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2078− sample (SpiderBehavior b) = readBehaviorUntracked b
1934+#endif
2079−
1935+
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)
1936+data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)
1937+
2126−newtype SpiderEventHandle a = SpiderEventHandle { unSpiderEventHandle :: EventSubscription a }
1938+data SpiderEventHandle x a = SpiderEventHandle
2127−
1939+ { spiderEventHandleSubscription :: EventSubscription x
2128−instance R.MonadSubscribeEvent (SpiderTimeline x) (SpiderHostFrame x) where
1940+ , spiderEventHandleValue :: IORef (Maybe a)
2129− {-# INLINABLE subscribeEvent #-}
1941+ }
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
2146
1942
2147instance MonadRef (EventM x) where
1943instance MonadRef (EventM x) where
2148 type Ref (EventM x) = Ref IO
1944 type Ref (EventM x) = Ref IO
@@ -2158,7 +1954,7 @@ instance MonadAtomicRef (EventM x) where
2158 atomicModifyRef r f = liftIO $ atomicModifyRef r f
1954 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2159
1955
2160-- | The monad for actions that manipulate a Spider timeline identified by @x@
1956-- | 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)
2162
1958
2163instance Monad (SpiderHost x) where
1959instance Monad (SpiderHost x) where
2164 {-# INLINABLE (>>=) #-}
1960 {-# INLINABLE (>>=) #-}
@@ -2173,11 +1969,11 @@ instance Monad (SpiderHost x) where
2173-- | Run an action affecting the global Spider timeline; this will be guarded by
1969-- | Run an action affecting the global Spider timeline; this will be guarded by
2174-- a mutex for that timeline
1970-- a mutex for that timeline
2175runSpiderHost :: SpiderHost Global a -> IO a
1971runSpiderHost :: SpiderHost Global a -> IO a
2176−runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimeline
1972+runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimelineEnv
2177
1973
2178-- | Run an action affecting a given Spider timeline; this will be guarded by a
1974-- | Run an action affecting a given Spider timeline; this will be guarded by a
2179-- mutex for that timeline
1975-- mutex for that timeline
2180−runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimeline x -> IO a
1976+runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2181runSpiderHostForTimeline (SpiderHost a) = runReaderT a
1977runSpiderHostForTimeline (SpiderHost a) = runReaderT a
2182
1978
2183newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
1979newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
@@ -2192,27 +1988,12 @@ instance Monad (SpiderHostFrame x) where
2192 {-# INLINABLE fail #-}
1988 {-# INLINABLE fail #-}
2193 fail s = SpiderHostFrame $ fail s
1989 fail s = SpiderHostFrame $ fail s
2194
1990
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)
2211newEventWithTriggerIO f = do
1992newEventWithTriggerIO f = do
2212 es <- newFanEventWithTriggerIO $ \Refl -> f
1993 es <- newFanEventWithTriggerIO $ \Refl -> f
2213 return $ select es Refl
1994 return $ select es Refl
2214
1995
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)
2216newFanEventWithTriggerIO f = do
1997newFanEventWithTriggerIO f = do
2217 occRef <- newIORef DMap.empty
1998 occRef <- newIORef DMap.empty
2218 subscribedRef <- newIORef DMap.empty
1999 subscribedRef <- newIORef DMap.empty
@@ -2221,48 +2002,10 @@ newFanEventWithTriggerIO f = do
2221 , rootSubscribed = subscribedRef
2002 , rootSubscribed = subscribedRef
2222 , rootInit = f
2003 , rootInit = f
2223 }
2004 }
2224− return $ EventSelector $ \k -> EventRoot k r
2005+ 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
2246
2006
2247newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2007newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2248
2008
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−
2266instance MonadRef (SpiderHost x) where
2009instance MonadRef (SpiderHost x) where
2267 type Ref (SpiderHost x) = Ref IO
2010 type Ref (SpiderHost x) = Ref IO
2268 newRef = SpiderHost . newRef
2011 newRef = SpiderHost . newRef
@@ -2281,9 +2024,6 @@ instance MonadRef (SpiderHostFrame x) where
2281instance MonadAtomicRef (SpiderHostFrame x) where
2024instance MonadAtomicRef (SpiderHostFrame x) where
2282 atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2025 atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2283
2026
2284−--------------------------------------------------------------------------------
2027+instance PrimMonad (SpiderHostFrame x) where
2285−-- Deprecated items
2028+ type PrimState (SpiderHostFrame x) = PrimState IO
2286−--------------------------------------------------------------------------------
2029+ primitive = SpiderHostFrame . EventM . primitive
2287−
2288−{-# DEPRECATED SpiderEnv "Use 'SpiderTimeline' instead" #-}
2289−type SpiderEnv = SpiderTimeline
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 #-}
1{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RankNTypes #-}
8+{-# LANGUAGE RecursiveDo #-}
7
9
8module Reflex.Test.Micro (testCases) where
10module Reflex.Test.Micro (testCases) where
9
11
@@ -12,16 +14,16 @@ import Reflex.TestPlan
12
14
13import Control.Applicative
15import Control.Applicative
14import Control.Monad
16import Control.Monad
17+import Control.Monad.Fix
15import Data.Char
18import Data.Char
16−import Data.Functor.Misc
17−import Data.Monoid
18−
19import Data.Foldable
19import Data.Foldable
20−
20+import Data.Functor.Misc
21import qualified Data.Map as Map
21import qualified Data.Map as Map
22+import Data.Monoid
22
23
23import Prelude
24import Prelude
24
25
26+{-# ANN testCases "HLint: ignore Functor law" #-}
25testCases :: [(String, TestCase)]
27testCases :: [(String, TestCase)]
26testCases =
28testCases =
27 [ testB "hold" $ hold "0" =<< events1
29 [ testB "hold" $ hold "0" =<< events1
@@ -50,6 +52,13 @@ testCases =
50 pushAlways (const $ hold "z" es) e
52 pushAlways (const $ hold "z" es) e
51 return (join b)
53 return (join b)
52
54
55+ , testE "id" $ do
56+ events2
57+
58+ , testE "fmap-id" $ do
59+ e <- events2
60+ return $ fmap id e
61+
53 , testE "tag-1" $ do
62 , testE "tag-1" $ do
54 b1 <- behavior1
63 b1 <- behavior1
55 e <- events2
64 e <- events2
@@ -247,6 +256,14 @@ testCases =
247 e2 <- events2
256 e2 <- events2
248 return $ e1 `difference ` e2
257 return $ e1 `difference ` e2
249
258
259+ , testE "lazy-hold" $ do
260+ let lazyHold :: forall t m. (Reflex t, MonadHold t m, MonadFix m) => m (Event t ())
261+ lazyHold = do
262+ rec !b <- hold never e
263+ let e = never <$ switch b
264+ return $ void e
265+ lazyHold
266+
250 ] where
267 ] where
251
268
252 events1, events2, events3 :: TestPlan t m => m (Event t String)
269 events1, events2, events3 :: TestPlan t m => m (Event t String)