on develop help sign in
Merge remote-tracking branch 'origin/functor-dynamic' into develop
2401b74Ryan Trinkle
@@ -1,4 +1,2 @@
1:set -isrc
1:set -isrc
2−:set -hide-package MonadCatchIO-mtl
3−:set -hide-package monads-fd
4:seti -XOverloadedStrings
2:seti -XOverloadedStrings
.gitignore+2
@@ -29,3 +29,5 @@ tags
29hsenv.log
29hsenv.log
30\#*#
30\#*#
31.#*
31.#*
32+/shell.nix
33+/ghci-tmp
.stylish-haskell.yamlnew file+154
@@ -0,0 +1,154 @@
1+# stylish-haskell configuration file
2+# ==================================
3+
4+# The stylish-haskell tool is mainly configured by specifying steps. These steps
5+# are a list, so they have an order, and one specific step may appear more than
6+# once (if needed). Each file is processed by these steps in the given order.
7+steps:
8+ # Convert some ASCII sequences to their Unicode equivalents. This is disabled
9+ # by default.
10+ # - unicode_syntax:
11+ # # In order to make this work, we also need to insert the UnicodeSyntax
12+ # # language pragma. If this flag is set to true, we insert it when it's
13+ # # not already present. You may want to disable it if you configure
14+ # # language extensions using some other method than pragmas. Default:
15+ # # true.
16+ # add_language_pragma: true
17+
18+ # Import cleanup
19+ - imports:
20+ # There are different ways we can align names and lists.
21+ #
22+ # - global: Align the import names and import list throughout the entire
23+ # file.
24+ #
25+ # - file: Like global, but don't add padding when there are no qualified
26+ # imports in the file.
27+ #
28+ # - group: Only align the imports per group (a group is formed by adjacent
29+ # import lines).
30+ #
31+ # - none: Do not perform any alignment.
32+ #
33+ # Default: global.
34+ align: none
35+
36+ # Folowing options affect only import list alignment.
37+ #
38+ # List align has following options:
39+ #
40+ # - after_alias: Import list is aligned with end of import including
41+ # 'as' and 'hiding' keywords.
42+ #
43+ # > import qualified Data.List as List (concat, foldl, foldr, head,
44+ # > init, last, length)
45+ #
46+ # - with_alias: Import list is aligned with start of alias or hiding.
47+ #
48+ # > import qualified Data.List as List (concat, foldl, foldr, head,
49+ # > init, last, length)
50+ #
51+ # - new_line: Import list starts always on new line.
52+ #
53+ # > import qualified Data.List as List
54+ # > (concat, foldl, foldr, head, init, last, length)
55+ #
56+ # Default: after alias
57+ list_align: after_alias
58+
59+ # Long list align style takes effect when import is too long. This is
60+ # determined by 'columns' setting.
61+ #
62+ # - inline: This option will put as much specs on same line as possible.
63+ #
64+ # - new_line: Import list will start on new line.
65+ #
66+ # - new_line_multiline: Import list will start on new line when it's
67+ # short enough to fit to single line. Otherwise it'll be multiline.
68+ #
69+ # - multiline: One line per import list entry.
70+ # Type with contructor list acts like single import.
71+ #
72+ # > import qualified Data.Map as M
73+ # > ( empty
74+ # > , singleton
75+ # > , ...
76+ # > , delete
77+ # > )
78+ #
79+ # Default: inline
80+ long_list_align: inline
81+
82+ # List padding determines indentation of import list on lines after import.
83+ # This option affects 'list_align' and 'long_list_align'.
84+ list_padding: 2
85+
86+ # Separate lists option affects formating of import list for type
87+ # or class. The only difference is single space between type and list
88+ # of constructors, selectors and class functions.
89+ #
90+ # - true: There is single space between Foldable type and list of it's
91+ # functions.
92+ #
93+ # > import Data.Foldable (Foldable (fold, foldl, foldMap))
94+ #
95+ # - false: There is no space between Foldable type and list of it's
96+ # functions.
97+ #
98+ # > import Data.Foldable (Foldable(fold, foldl, foldMap))
99+ #
100+ # Default: true
101+ separate_lists: true
102+
103+ # Language pragmas
104+ - language_pragmas:
105+ # We can generate different styles of language pragma lists.
106+ #
107+ # - vertical: Vertical-spaced language pragmas, one per line.
108+ #
109+ # - compact: A more compact style.
110+ #
111+ # - compact_line: Similar to compact, but wrap each line with
112+ # `{-#LANGUAGE #-}'.
113+ #
114+ # Default: vertical.
115+ style: vertical
116+
117+ # Align affects alignment of closing pragma brackets.
118+ #
119+ # - true: Brackets are aligned in same collumn.
120+ #
121+ # - false: Brackets are not aligned together. There is only one space
122+ # between actual import and closing bracket.
123+ #
124+ # Default: true
125+ align: false
126+
127+ # stylish-haskell can detect redundancy of some language pragmas. If this
128+ # is set to true, it will remove those redundant pragmas. Default: true.
129+ remove_redundant: true
130+
131+ # Align the types in record declarations
132+ # - records: {}
133+
134+ # Replace tabs by spaces. This is disabled by default.
135+ # - tabs:
136+ # # Number of spaces to use for each tab. Default: 8, as specified by the
137+ # # Haskell report.
138+ # spaces: 8
139+
140+ # Remove trailing whitespace
141+ - trailing_whitespace: {}
142+
143+# A common setting is the number of columns (parts of) code will be wrapped
144+# to. Different steps take this into account. Default: 80.
145+columns: 110
146+
147+# Sometimes, language extensions are specified in a cabal file or from the
148+# command line instead of using language pragmas in the file. stylish-haskell
149+# needs to be aware of these, so it can parse the file correctly.
150+#
151+# No language extensions are enabled by default.
152+# language_extensions:
153+ # - TemplateHaskell
154+ # - QuasiQuotes
.travis.yml+3−1
@@ -6,7 +6,7 @@ install:
6 - travis_retry cabal-$CABALVER install --only-dependencies -j --enable-tests
6 - travis_retry cabal-$CABALVER install --only-dependencies -j --enable-tests
7script:
7script:
8 - cabal-$CABALVER configure -v2 --enable-tests
8 - cabal-$CABALVER configure -v2 --enable-tests
9− - cabal-$CABALVER build
9+ - travis_wait cabal-$CABALVER build
10 - cabal-$CABALVER test
10 - cabal-$CABALVER test
11 - cabal-$CABALVER sdist # tests that a source-distribution can be generated
11 - cabal-$CABALVER sdist # tests that a source-distribution can be generated
12# Check that the resulting source distribution can be built & installed.
12# Check that the resulting source distribution can be built & installed.
@@ -21,4 +21,6 @@ matrix:
21 addons: {apt: {packages: [cabal-install-1.18, ghc-7.8.4, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}
21 addons: {apt: {packages: [cabal-install-1.18, ghc-7.8.4, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}
22 - env: CABALVER=1.22 GHCVER=7.10.1
22 - env: CABALVER=1.22 GHCVER=7.10.1
23 addons: {apt: {packages: [cabal-install-1.22, ghc-7.10.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
23 addons: {apt: {packages: [cabal-install-1.22, ghc-7.10.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
24+ - env: CABALVER=1.24 GHCVER=8.0.1
25+ addons: {apt: {packages: [cabal-install-1.24, ghc-8.0.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
24 fast_finish: true
26 fast_finish: true
Quickref.md+1
@@ -127,6 +127,7 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
127[H] mconcatDyn :: Monoid a => [Dynamic a] -> m (Dynamic a)
127[H] mconcatDyn :: Monoid a => [Dynamic a] -> m (Dynamic a)
128[H] distributeDMapOverDyn :: GCompare k => DMap (WrapArg Dynamic k) -> m (Dynamic (DMap k))
128[H] distributeDMapOverDyn :: GCompare k => DMap (WrapArg Dynamic k) -> m (Dynamic (DMap k))
129[H] combineDyn :: (a -> b -> c) -> Dynamic a -> Dynamic b -> m (Dynamic c)
129[H] combineDyn :: (a -> b -> c) -> Dynamic a -> Dynamic b -> m (Dynamic c)
130+[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
130
131
131-- Efficient one-to-many fanout
132-- Efficient one-to-many fanout
132[ ] demux :: Ord k => Dynamic k -> Demux k
133[ ] demux :: Ord k => Dynamic k -> Demux k
bench/Main.hs+33−35
@@ -1,38 +1,36 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE TupleSections #-}
2+{-# LANGUAGE CPP #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE TupleSections #-}
4{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
6+
7+-- The instance for NFData (TVar a) is an orphan, but necessary here
8+{-# OPTIONS_GHC -fno-warn-orphans #-}
9+
5module Main where
10module Main where
6
11
7−import Data.Functor.Misc
8−import Control.Monad.Primitive
9−import Control.Monad.IO.Class
10−import Control.Monad.Identity
11−import Data.Dependent.Sum
12import Control.Concurrent.STM
12import Control.Concurrent.STM
13−import Control.Applicative
14−import System.IO.Unsafe
15−import Data.IORef
16import Control.DeepSeq
13import Control.DeepSeq
17import Control.Exception (evaluate)
14import Control.Exception (evaluate)
18−import Control.Monad
15+import Control.Monad.Identity
16+import Control.Monad.IO.Class
17+import Criterion.Main
18+import Data.Dependent.Map (DMap)
19+import qualified Data.Dependent.Map as DMap
20+import Data.Dependent.Sum
21+import Data.Functor.Misc
22+import Data.IORef
19import Reflex
23import Reflex
20import Reflex.Host.Class
24import Reflex.Host.Class
21−import System.Mem
22−import System.IO
23−import Criterion.Main
24−
25−import qualified Data.Traversable as T
26−
27−import qualified Data.Dependent.Map as DM
28−
29
25
30main :: IO ()
26main :: IO ()
31main = defaultMain
27main = defaultMain
32 [ bgroup "micro" micros ]
28 [ bgroup "micro" micros ]
33
29
30+#if !(MIN_VERSION_deepseq(1,4,2))
34instance NFData (IORef a) where
31instance NFData (IORef a) where
35 rnf x = seq x ()
32 rnf x = seq x ()
33+#endif
36
34
37instance NFData (TVar a) where
35instance NFData (TVar a) where
38 rnf x = seq x ()
36 rnf x = seq x ()
@@ -41,15 +39,14 @@ newtype WHNF a = WHNF a
41instance NFData (WHNF a) where
39instance NFData (WHNF a) where
42 rnf (WHNF a) = seq a ()
40 rnf (WHNF a) = seq a ()
43
41
44−withSetup :: NFData b => String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
42+withSetup :: NFData b => String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
45withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
43withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
46 bench name . nfIO $ runSpiderHost (action a)
44 bench name . nfIO $ runSpiderHost (action a)
47
45
48−withSetupWHNF :: String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
46+withSetupWHNF :: String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
49withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
47withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
50 bench name . whnfIO $ runSpiderHost (action a)
48 bench name . whnfIO $ runSpiderHost (action a)
51
49
52−
53micros :: [Benchmark]
50micros :: [Benchmark]
54micros =
51micros =
55 [ bench "newIORef" $ whnfIO $ void $ newIORef ()
52 [ bench "newIORef" $ whnfIO $ void $ newIORef ()
@@ -72,40 +69,41 @@ micros =
72 (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
69 (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
73 , withSetupWHNF "fireEventsOnly"
70 , withSetupWHNF "fireEventsOnly"
74 (newEventWithTriggerRef >>= subscribePair)
71 (newEventWithTriggerRef >>= subscribePair)
75− (\(subd, trigger) -> do
72+ (\(_, trigger) -> do
76 Just key <- liftIO $ readIORef trigger
73 Just key <- liftIO $ readIORef trigger
77 fireEvents [key :=> Identity (42 :: Int)])
74 fireEvents [key :=> Identity (42 :: Int)])
78 , withSetupWHNF "fireEventsAndRead(head/merge1)"
75 , withSetupWHNF "fireEventsAndRead(head/merge1)"
79 (setupMerge 1 >>= subscribePair)
76 (setupMerge 1 >>= subscribePair)
80− (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
77+ (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
81 , withSetupWHNF "fireEventsAndRead(head/merge100)"
78 , withSetupWHNF "fireEventsAndRead(head/merge100)"
82 (setupMerge 100 >>= subscribePair)
79 (setupMerge 100 >>= subscribePair)
83− (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
80+ (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
84 , withSetupWHNF "fireEventsAndRead(head/merge10000)"
81 , withSetupWHNF "fireEventsAndRead(head/merge10000)"
85 (setupMerge 10000 >>= subscribePair)
82 (setupMerge 10000 >>= subscribePair)
86− (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
83+ (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
87 , withSetupWHNF "fireEventsOnly(head/merge100)"
84 , withSetupWHNF "fireEventsOnly(head/merge100)"
88 (setupMerge 100 >>= subscribePair)
85 (setupMerge 100 >>= subscribePair)
89− (\(subd, t:riggers) -> do
86+ (\(_, t:_) -> do
90 Just key <- liftIO $ readIORef t
87 Just key <- liftIO $ readIORef t
91 fireEvents [key :=> Identity (42 :: Int)])
88 fireEvents [key :=> Identity (42 :: Int)])
92− , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev,trigger) -> hold (42 :: Int) ev
89+ , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev, _) -> hold (42 :: Int) ev
93 , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
90 , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
94 ]
91 ]
95
92
96setupMerge :: Int
93setupMerge :: Int
97− -> SpiderHost (Event Spider (DM.DMap (Const2 Int a) Identity),
94+ -> SpiderHost Global ( Event (SpiderEnv Global) (DMap (Const2 Int a) Identity)
98− [IORef (Maybe (EventTrigger Spider a))])
95+ , [IORef (Maybe (EventTrigger Spider a))]
96+ )
99setupMerge num = do
97setupMerge num = do
100− (evs, triggers) <- unzip <$> replicateM 100 newEventWithTriggerRef
98+ (evs, triggers) <- unzip <$> replicateM num newEventWithTriggerRef
101− let !m = DM.fromList [(Const2 i) :=> v | (i,v) <- zip [0..] evs]
99+ let !m = DMap.fromList [Const2 i :=> v | (i,v) <- zip [0..] evs]
102 pure (merge m, triggers)
100 pure (merge m, triggers)
103
101
104−subscribePair :: (Event Spider a, b) -> SpiderHost (EventHandle Spider a, b)
102+subscribePair :: (Event (SpiderEnv Global) a, b) -> SpiderHost Global (EventHandle (SpiderEnv Global) a, b)
105subscribePair (ev, b) = (,b) <$> subscribeEvent ev
103subscribePair (ev, b) = (,b) <$> subscribeEvent ev
106
104
107−fireAndRead :: IORef (Maybe (EventTrigger Spider a)) -> a -> EventHandle Spider b
105+fireAndRead :: IORef (Maybe (EventTrigger (SpiderEnv Global) a)) -> a -> EventHandle (SpiderEnv Global) b
108− -> SpiderHost (Maybe b)
106+ -> SpiderHost Global (Maybe b)
109fireAndRead trigger val subd = do
107fireAndRead trigger val subd = do
110 Just key <- liftIO $ readIORef trigger
108 Just key <- liftIO $ readIORef trigger
111− fireEventsAndRead [key :=> Identity val] $ readEvent subd >>= T.sequence
109+ fireEventsAndRead [key :=> Identity val] $ readEvent subd >>= sequence
bench/RunAll.hs+34−21
@@ -1,4 +1,18 @@
1−{-# LANGUAGE ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving, ForeignFunctionInterface, ViewPatterns, TemplateHaskell, PatternSynonyms #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE ForeignFunctionInterface #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE PatternSynonyms #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TemplateHaskell #-}
13+{-# LANGUAGE TupleSections #-}
14+{-# LANGUAGE TypeSynonymInstances #-}
15+{-# LANGUAGE ViewPatterns #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
16{-# OPTIONS_GHC -fno-warn-orphans #-}
3
17
4module Main where
18module Main where
@@ -9,38 +23,37 @@ import Criterion.Types
9import Reflex
23import Reflex
10import Reflex.Host.Class
24import Reflex.Host.Class
11
25
12−import Reflex.TestPlan
13import Reflex.Plan.Reflex
26import Reflex.Plan.Reflex
27+import Reflex.TestPlan
14
28
15−import Reflex.Spider.Internal (SpiderEventHandle)
16import qualified Reflex.Bench.Focused as Focused
29import qualified Reflex.Bench.Focused as Focused
30+import Reflex.Spider.Internal (SpiderEventHandle)
17
31
18import Control.Applicative
32import Control.Applicative
19import Control.DeepSeq (NFData (..))
33import Control.DeepSeq (NFData (..))
20
34
35+import Prelude
21import System.IO
36import System.IO
22import System.Mem
37import System.Mem
23−import Prelude
24
38
25−import Data.IORef
26−import System.Mem.Weak
27−import Data.Function
28−import Control.Concurrent
29−import Data.Time.Clock
30−import GHC.Stats
31−import Data.Monoid
32import Control.Arrow
39import Control.Arrow
40+import Control.Concurrent
41+import Control.Concurrent.STM
42+import Control.Exception
33import Control.Monad
43import Control.Monad
44+import Control.Monad.Trans
34import Data.Bool
45import Data.Bool
35−import qualified GHC.Event as GHC
46+import Data.Function
36−import Control.Concurrent.STM
37import Data.Int
47import Data.Int
38−import Control.Exception
48+import Data.IORef
39−import Text.Read
49+import Data.Monoid
40−import System.Environment
50+import Data.Time.Clock
41import Debug.Trace.LocationTH
51import Debug.Trace.LocationTH
42−import Control.Monad.Trans
52+import GHC.Stats
53+import System.Environment
54+import System.Mem.Weak
43import System.Process
55import System.Process
56+import Text.Read
44
57
45import Unsafe.Coerce
58import Unsafe.Coerce
46
59
@@ -68,7 +81,7 @@ instance NFData (Behavior t a) where
68 rnf !_ = ()
81 rnf !_ = ()
69
82
70instance NFData (Firing t) where
83instance NFData (Firing t) where
71− rnf !(Firing _ _) = ()
84+ rnf !_ = ()
72
85
73-- Measure the running time
86-- Measure the running time
74benchFiring :: forall t m. (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> TestCase -> Int -> IO ()
87benchFiring :: forall t m. (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> TestCase -> Int -> IO ()
@@ -80,10 +93,10 @@ benchFiring runHost tc n = runHost $ do
80 case tc of
93 case tc of
81 TestE p -> do
94 TestE p -> do
82 (h, s) <- setupFiring p
95 (h, s) <- setupFiring p
83− runIterations $ readSchedule s $ readEvent' h
96+ runIterations $ readSchedule_ s $ readEvent' h
84 TestB p -> do
97 TestB p -> do
85 (b, s) <- runPlan p
98 (b, s) <- runPlan p
86− runIterations $ readSchedule (makeDense s) $ sample b
99+ runIterations $ readSchedule_ (makeDense s) $ sample b
87
100
88waitForFinalizers :: IO ()
101waitForFinalizers :: IO ()
89waitForFinalizers = do
102waitForFinalizers = do
@@ -94,7 +107,7 @@ waitForFinalizers = do
94 performGC
107 performGC
95 fix $ \loop -> do
108 fix $ \loop -> do
96 f <- readIORef isFinalized
109 f <- readIORef isFinalized
97− if f then return () else do
110+ unless f $ do
98 threadDelay 1
111 threadDelay 1
99 loop
112 loop
100
113
default.nix+3−1
@@ -2,7 +2,7 @@
2, exception-transformers, haskell-src-exts, haskell-src-meta
2, exception-transformers, haskell-src-exts, haskell-src-meta
3, MemoTrie, mtl, primitive, ref-tf, semigroups, stdenv, syb
3, MemoTrie, mtl, primitive, ref-tf, semigroups, stdenv, syb
4, template-haskell, these, transformers, transformers-compat
4, template-haskell, these, transformers, transformers-compat
5−, criterion, deepseq, split, stm, loch-th
5+, criterion, deepseq, split, stm, loch-th, hlint
6}:
6}:
7mkDerivation {
7mkDerivation {
8 pname = "reflex";
8 pname = "reflex";
@@ -17,6 +17,8 @@ mkDerivation {
17 base containers dependent-map MemoTrie mtl ref-tf
17 base containers dependent-map MemoTrie mtl ref-tf
18 # Benchmark dependencies
18 # Benchmark dependencies
19 criterion deepseq split stm loch-th
19 criterion deepseq split stm loch-th
20+ # HLint
21+ hlint
20 ];
22 ];
21 homepage = "https://github.com/reflex-frp/reflex";
23 homepage = "https://github.com/reflex-frp/reflex";
22 description = "Higher-order Functional Reactive Programming";
24 description = "Higher-order Functional Reactive Programming";
reflex.cabal+26−11
@@ -17,9 +17,11 @@ library
17 hs-source-dirs: src
17 hs-source-dirs: src
18 build-depends:
18 build-depends:
19 base >= 4.7 && < 4.10,
19 base >= 4.7 && < 4.10,
20+ bifunctors >= 5.2 && < 5.5,
20 dependent-sum == 0.3.*,
21 dependent-sum == 0.3.*,
21− dependent-map == 0.2.*,
22+ dependent-map >= 0.2.2 && < 0.3,
22 semigroups >= 0.16 && < 0.19,
23 semigroups >= 0.16 && < 0.19,
24+ semigroupoids >= 4.0 && < 6,
23 mtl >= 2.1 && < 2.3,
25 mtl >= 2.1 && < 2.3,
24 containers == 0.5.*,
26 containers == 0.5.*,
25 these >= 0.4 && < 0.8,
27 these >= 0.4 && < 0.8,
@@ -31,7 +33,8 @@ library
31 transformers-compat >= 0.3,
33 transformers-compat >= 0.3,
32 haskell-src-exts >= 1.16 && < 1.18,
34 haskell-src-exts >= 1.16 && < 1.18,
33 haskell-src-meta == 0.6.*,
35 haskell-src-meta == 0.6.*,
34− syb >= 0.5 && < 0.7
36+ syb >= 0.5 && < 0.7,
37+ stm == 2.4.*
35
38
36 exposed-modules:
39 exposed-modules:
37 Reflex,
40 Reflex,
@@ -39,18 +42,21 @@ library
39 Reflex.Spider.Internal,
42 Reflex.Spider.Internal,
40 Reflex.Class,
43 Reflex.Class,
41 Reflex.Dynamic,
44 Reflex.Dynamic,
45+ Reflex.Dynamic.Uniq,
42 Reflex.Dynamic.TH,
46 Reflex.Dynamic.TH,
43 Reflex.Host.Class,
47 Reflex.Host.Class,
44− Data.Functor.Misc
48+ Data.Functor.Misc,
49+ Data.WeakBag
45
50
46 other-extensions: TemplateHaskell
51 other-extensions: TemplateHaskell
47 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
52 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
53+ ghc-prof-options: -auto-all
48
54
49
55
50test-suite all
56test-suite all
51 type: exitcode-stdio-1.0
57 type: exitcode-stdio-1.0
52 main-is: Reflex/Test/RunAll.hs
58 main-is: Reflex/Test/RunAll.hs
53− ghc-options: -O2 -Wall
59+ ghc-options: -O2 -Wall -rtsopts
54 hs-source-dirs: test
60 hs-source-dirs: test
55 build-depends:
61 build-depends:
56 base,
62 base,
@@ -60,8 +66,9 @@ test-suite all
60 containers,
66 containers,
61 dependent-map,
67 dependent-map,
62 dependent-sum,
68 dependent-sum,
63− transformers >= 0.3 && < 0.5,
69+ transformers >= 0.3,
64 MemoTrie == 0.6.*,
70 MemoTrie == 0.6.*,
71+ deepseq >= 1.3 && < 1.5,
65 bifunctors,
72 bifunctors,
66 split
73 split
67
74
@@ -70,33 +77,35 @@ benchmark spider-bench
70 type: exitcode-stdio-1.0
77 type: exitcode-stdio-1.0
71 hs-source-dirs: bench
78 hs-source-dirs: bench
72 main-is: Main.hs
79 main-is: Main.hs
73− ghc-options: -O2 -rtsopts
80+ ghc-options: -Wall -O2 -rtsopts
74 build-depends:
81 build-depends:
75 base,
82 base,
76 dependent-sum,
83 dependent-sum,
77 dependent-map,
84 dependent-map,
78− transformers >= 0.3 && < 0.5,
85+ transformers >= 0.3,
79− stm == 2.4.*,
86+ stm,
80 deepseq >= 1.3 && < 1.5,
87 deepseq >= 1.3 && < 1.5,
81 mtl,
88 mtl,
82 primitive,
89 primitive,
83 criterion == 1.1.*,
90 criterion == 1.1.*,
84 reflex
91 reflex
85
92
93+
86benchmark saulzar-bench
94benchmark saulzar-bench
87 type: exitcode-stdio-1.0
95 type: exitcode-stdio-1.0
88 hs-source-dirs: bench test
96 hs-source-dirs: bench test
89 c-sources: bench-cbits/checkCapability.c
97 c-sources: bench-cbits/checkCapability.c
90 main-is: RunAll.hs
98 main-is: RunAll.hs
91− ghc-options: -O2 -rtsopts -threaded
99+ ghc-options: -Wall -O2 -rtsopts -threaded
100+ ghc-prof-options: -auto-all
92 build-depends:
101 build-depends:
93 base,
102 base,
94 containers == 0.5.*,
103 containers == 0.5.*,
95 ref-tf == 0.4,
104 ref-tf == 0.4,
96 dependent-sum,
105 dependent-sum,
97 dependent-map,
106 dependent-map,
98− transformers >= 0.3 && < 0.5,
107+ transformers >= 0.3,
99− stm == 2.4.*,
108+ stm,
100 deepseq >= 1.3 && < 1.5,
109 deepseq >= 1.3 && < 1.5,
101 mtl,
110 mtl,
102 primitive,
111 primitive,
@@ -107,6 +116,12 @@ benchmark saulzar-bench
107 loch-th,
116 loch-th,
108 process
117 process
109
118
119+test-suite hlint
120+ build-depends: base, hlint == 1.9.*
121+ hs-source-dirs: test
122+ main-is: hlint.hs
123+ type: exitcode-stdio-1.0
124+
110source-repository head
125source-repository head
111 type: git
126 type: git
112 location: https://github.com/reflex-frp/reflex
127 location: https://github.com/reflex-frp/reflex
src/Data/Functor/Misc.hs+134−41
@@ -1,31 +1,65 @@
1−{-# LANGUAGE KindSignatures, GADTs, DeriveDataTypeable, RankNTypes, ScopedTypeVariables, PolyKinds #-}
1+{-# LANGUAGE DeriveDataTypeable #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PolyKinds #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE StandaloneDeriving #-}
11+-- | This module provides types and functions with no particular theme, but
12+-- which are relevant to the use of 'Functor'-based datastructures like
13+-- 'Data.Dependent.Map.DMap'.
2−module Data.Functor.Misc where
14+module Data.Functor.Misc
3−
15+ ( -- * Const2
16+ Const2 (..)
17+ , dmapToMap
18+ , mapToDMap
19+ -- * WrapArg
20+ , WrapArg (..)
21+ -- * Convenience functions for DMap
22+ , mapWithFunctorToDMap
23+ , combineDMapsWithKey
24+ -- * Deprecated functions
25+ , sequenceDmap
26+ , wrapDMap
27+ , rewrapDMap
28+ , unwrapDMap
29+ , unwrapDMapMaybe
30+ , extractFunctorDMap
31+ ) where
32+
33+import Control.Applicative ((<$>), Applicative)
34+import Control.Monad.Identity
35+import Data.Dependent.Map (DMap)
36+import qualified Data.Dependent.Map as DMap
37+import Data.Dependent.Sum
4import Data.GADT.Compare
38import Data.GADT.Compare
39+import Data.GADT.Show
5import Data.Map (Map)
40import Data.Map (Map)
6import qualified Data.Map as Map
41import qualified Data.Map as Map
7−import Data.Dependent.Map (DMap, DSum (..))
8−import qualified Data.Dependent.Map as DMap
9−import Data.Typeable hiding (Refl)
10import Data.These
42import Data.These
11−import Control.Monad.Identity
43+import Data.Typeable hiding (Refl)
12−
13−data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
14− WrapArg :: f a -> WrapArg g f (g a)
15−
16−instance GEq f => GEq (WrapArg g f) where
17− geq (WrapArg a) (WrapArg b) = fmap (\Refl -> Refl) $ geq a b
18
44
19−instance GCompare f => GCompare (WrapArg g f) where
45+--------------------------------------------------------------------------------
20− gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
46+-- Const2
21− GLT -> GLT
47+--------------------------------------------------------------------------------
22− GEQ -> GEQ
23− GGT -> GGT
24
48
49+-- | 'Const2' stores a value of a given type 'k' and ensures that a particular
50+-- type 'v' is always given for the last type parameter
25−data Const2 :: * -> * -> * -> * where
51+data Const2 :: * -> x -> x -> * where
26 Const2 :: k -> Const2 k v v
52 Const2 :: k -> Const2 k v v
27 deriving (Typeable)
53 deriving (Typeable)
28
54
55+deriving instance Show k => Show (Const2 k v v)
56+
57+instance Show k => GShow (Const2 k v) where
58+ gshowsPrec n x@(Const2 _) = showsPrec n x
59+
60+instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
61+ showTaggedPrec (Const2 _) = showsPrec
62+
29instance Eq k => GEq (Const2 k v) where
63instance Eq k => GEq (Const2 k v) where
30 geq (Const2 a) (Const2 b) =
64 geq (Const2 a) (Const2 b) =
31 if a == b
65 if a == b
@@ -38,13 +72,57 @@ instance Ord k => GCompare (Const2 k v) where
38 EQ -> GEQ
72 EQ -> GEQ
39 GT -> GGT
73 GT -> GGT
40
74
41−{-# INLINE sequenceDmap #-}
75+-- | Convert a 'DMap' to a regular 'Map'
42−sequenceDmap :: (Monad m, GCompare f) => DMap f m -> m (DMap f Identity)
76+dmapToMap :: DMap (Const2 k v) Identity -> Map k v
43−sequenceDmap = DMap.foldrWithKey (\k mv mx -> mx >>= \x -> mv >>= \v -> return $ DMap.insert k (Identity v) x)
77+dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
44− (return DMap.empty)
78+
79+-- | Convert a regular 'Map' to a 'DMap'
80+mapToDMap :: Map k v -> DMap (Const2 k v) Identity
81+mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
82+
83+-- | Convert a regular 'Map', where the values are already wrapped in a functor,
84+-- to a 'DMap'
85+mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
86+mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
87+
88+--------------------------------------------------------------------------------
89+-- WrapArg
90+--------------------------------------------------------------------------------
91+
92+-- | 'WrapArg' can be used to tag a value in one functor with a type
93+-- representing another functor. This was primarily used with dependent-map <
94+-- 0.2, in which the value type was not wrapped in a separate functor.
95+data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
96+ WrapArg :: f a -> WrapArg g f (g a)
97+
98+instance GEq f => GEq (WrapArg g f) where
99+ geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
100+
101+instance GCompare f => GCompare (WrapArg g f) where
102+ gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
103+ GLT -> GLT
104+ GEQ -> GEQ
105+ GGT -> GGT
106+
107+--------------------------------------------------------------------------------
108+-- Convenience functions for DMap
109+--------------------------------------------------------------------------------
110+
111+-- | Map over all key/value pairs in a 'DMap', potentially altering the key as
112+-- well as the value. The provided function MUST preserve the ordering of the
113+-- keys, or the resulting 'DMap' will be malformed.
114+mapKeyValuePairsMonotonic :: (DSum k v -> DSum k' v') -> DMap k v -> DMap k' v'
115+mapKeyValuePairsMonotonic f = DMap.fromDistinctAscList . map f . DMap.toAscList
45
116
46{-# INLINE combineDMapsWithKey #-}
117{-# INLINE combineDMapsWithKey #-}
118+-- | Union two 'DMap's of different types, yielding another type. Each key that
119+-- is present in either input map will be present in the output.
47−combineDMapsWithKey :: forall f g h i. GCompare f => (forall (a :: *). f a -> These (g a) (h a) -> i a) -> DMap f g -> DMap f h -> DMap f i
120+combineDMapsWithKey :: forall f g h i.
121+ GCompare f
122+ => (forall a. f a -> These (g a) (h a) -> i a)
123+ -> DMap f g
124+ -> DMap f h
125+ -> DMap f i
48combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
126combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
49 where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
127 where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
50 go [] hs = map (\(hk :=> hv) -> hk :=> f hk (That hv)) hs
128 go [] hs = map (\(hk :=> hv) -> hk :=> f hk (That hv)) hs
@@ -54,23 +132,38 @@ combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList m
54 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
132 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
55 GGT -> (hk :=> f hk (That hv)) : go gs hs'
133 GGT -> (hk :=> f hk (That hv)) : go gs hs'
56
134
57−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
135+--------------------------------------------------------------------------------
58−wrapDMap f = DMap.fromDistinctAscList . map (\(k :=> Identity v) -> k :=> f v) . DMap.toAscList
136+-- Deprecated functions
59−
137+--------------------------------------------------------------------------------
60−rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
61−rewrapDMap f = DMap.fromDistinctAscList . map (\(k :=> v) -> k :=> f v) . DMap.toAscList
62−
63−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
64−unwrapDMap f = DMap.fromDistinctAscList . map (\(k :=> v) -> k :=> Identity (f v)) . DMap.toAscList
65
138
66−unwrapDMapMaybe :: (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
139+{-# INLINE sequenceDmap #-}
67−unwrapDMapMaybe f m = DMap.fromDistinctAscList [k :=> Identity w | (k :=> v) <- DMap.toAscList m, Just w <- [f v]]
140+{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ t -> fmap Identity t)' instead" #-}
68−
141+-- | Run the actions contained in the 'DMap'
69−mapToDMap :: Map k v -> DMap (Const2 k v) Identity
142+sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
70−mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
143+sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
71
144
72−mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
145+{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
73−mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> (Const2 k) :=> v) . Map.toAscList
146+-- | Replace the 'Identity' functor for a 'DMap''s values with a different functor
147+wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
148+wrapDMap f = DMap.map $ f . runIdentity
74
149
75−dmapToMap :: DMap (Const2 k v) Identity -> Map k v
150+{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
76−dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
151+-- | Replace one functor for a 'DMap''s values with a different functor
152+rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
153+rewrapDMap = DMap.map
154+
155+{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}
156+-- | Replace one functor for a 'DMap''s values with the 'Identity' functor
157+unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
158+unwrapDMap f = DMap.map $ Identity . f
159+
160+{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}
161+-- | Like 'unwrapDMap', but possibly delete some values from the DMap
162+unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
163+unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
164+
165+{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
166+-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
167+-- underlying functor
168+extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
169+extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
src/Data/WeakBag.hsnew file+120
@@ -0,0 +1,120 @@
1+-- | 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.
3+-- This is useful for situations where a value needs to be inspected or modified
4+-- if it is still alive, but can be ignored if it is dead.
5+module Data.WeakBag
6+ ( WeakBag
7+ , WeakBagTicket
8+ , empty
9+ , singleton
10+ , insert
11+ , traverse
12+ , remove
13+ ) where
14+
15+import Control.Concurrent.STM
16+import Control.Exception
17+import Control.Monad hiding (forM_, mapM_)
18+import Control.Monad.IO.Class
19+import Data.Foldable (forM_, mapM_)
20+import Data.IntMap.Strict (IntMap)
21+import qualified Data.IntMap.Strict as IntMap
22+import Data.IORef
23+import System.Mem.Weak
24+
25+import Prelude hiding (mapM_, traverse)
26+
27+-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
28+-- 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.
30+data WeakBag a = WeakBag
31+ { _weakBag_nextId :: {-# UNPACK #-} !(TVar Int) --TODO: what if this wraps around?
32+ , _weakBag_children :: {-# UNPACK #-} !(TVar (IntMap (Weak a)))
33+ }
34+
35+-- | 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
37+-- 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'.
39+data WeakBagTicket a = WeakBagTicket
40+ { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
41+ , _weakBagTicket_item :: {-# NOUNPACK #-} !a
42+ }
43+
44+-- | Insert an item into a 'WeakBag'.
45+{-# INLINE insert #-}
46+insert :: a -- ^ The item
47+ -> WeakBag a -- ^ The 'WeakBag' to insert into
48+ -> IORef (Weak b) -- ^ An arbitrary value to be used in the following
49+ -- callback
50+ -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
51+ -- (whether automatically by the item being garbage
52+ -- collected or manually via 'remove')
53+ -> IO (WeakBagTicket a) -- ^ Returns a 'WeakBagTicket' that ensures the
54+ -- item is retained and allows the item to be
55+ -- removed.
56+insert a (WeakBag nextId children) wbRef finalizer = do
57+ a' <- evaluate a
58+ wbRef' <- evaluate wbRef
59+ myId <- atomically $ do
60+ myId <- readTVar nextId
61+ writeTVar nextId $! succ myId
62+ return myId
63+ let cleanup = do
64+ wb <- readIORef wbRef'
65+ mb <- deRefWeak wb
66+ forM_ mb $ \b -> do
67+ isLastNode <- atomically $ do --TODO: Should this run even when mb is Nothing?
68+ cs <- readTVar children
69+ let csWithoutMe = IntMap.delete myId cs
70+ writeTVar children $! csWithoutMe
71+ return $ IntMap.size csWithoutMe == 0
72+ when isLastNode $ finalizer b
73+ return ()
74+ return ()
75+ wa <- mkWeakPtr a' $ Just cleanup
76+ atomically $ modifyTVar' children $ IntMap.insert myId wa
77+ return $ WeakBagTicket
78+ { _weakBagTicket_weakItem = wa
79+ , _weakBagTicket_item = a'
80+ }
81+
82+-- | Create an empty 'WeakBag'.
83+{-# INLINE empty #-}
84+empty :: IO (WeakBag a)
85+empty = do
86+ nextId <- newTVarIO 1
87+ children <- newTVarIO IntMap.empty
88+ let bag = WeakBag
89+ { _weakBag_nextId = nextId
90+ , _weakBag_children = children
91+ }
92+ return bag
93+
94+-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
95+-- 'empty', then using 'insert'.
96+{-# INLINE singleton #-}
97+singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket a)
98+singleton a wbRef finalizer = do
99+ bag <- empty
100+ ticket <- insert a bag wbRef finalizer
101+ return (bag, ticket)
102+
103+{-# INLINE traverse #-}
104+-- | Visit every node in the given list. If new nodes are appended during the
105+-- traversal, they will not be visited. Every live node that was in the list
106+-- when the traversal began will be visited exactly once; however, no guarantee
107+-- is made about the order of the traversal.
108+traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
109+traverse (WeakBag _ children) f = do
110+ cs <- liftIO $ readTVarIO children
111+ forM_ cs $ \c -> do
112+ ma <- liftIO $ deRefWeak c
113+ mapM_ f ma
114+
115+-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
116+-- on the same 'WeakBagTicket'.
117+{-# INLINE remove #-}
118+remove :: WeakBagTicket a -> IO ()
119+remove = finalize . _weakBagTicket_weakItem
120+--TODO: Should 'remove' also drop the reference to the item?
src/Reflex.hs+8−9
@@ -1,10 +1,9 @@
1+-- | This module exports all of the commonly-used functionality of Reflex; if
2+-- you are just getting started with Reflex, this is probably what you want.
1−module Reflex ( module Reflex.Class
3+module Reflex (module X) where
2− , module Reflex.Dynamic
3− , module Reflex.Dynamic.TH
4− , module Reflex.Spider
5− ) where
6
4
7−import Reflex.Class
5+import Reflex.Class as X
6+import Reflex.Dynamic as X
8−import Reflex.Dynamic
7+import Reflex.Dynamic.Uniq as X
9−import Reflex.Dynamic.TH
8+import Reflex.Dynamic.TH as X
10−import Reflex.Spider
9+import Reflex.Spider as X
src/Reflex/Class.hs+527−122
@@ -1,67 +1,287 @@
1−{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, CPP #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE NoMonomorphismRestriction #-}
9+{-# LANGUAGE RankNTypes #-}
10+{-# LANGUAGE RecursiveDo #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE TypeOperators #-}
14+{-# LANGUAGE UndecidableInstances #-}
15+
16+-- | This module contains the Reflex interface, as well as a variety of
17+-- convenience functions for working with 'Event's, 'Behavior's, and other
18+-- signals.
2−module Reflex.Class where
19+module Reflex.Class
20+ ( -- * Primitives
21+ Reflex (..)
22+ , MonadSample (..)
23+ , MonadHold (..)
24+ -- ** 'fan'-related types
25+ , EventSelector (..)
26+ -- ** 'Incremental'-related types
27+ , Patch (..)
28+ , PatchDMap (..)
29+ -- * Convenience functions
30+ , constDyn
31+ , pushAlways
32+ -- ** Combining 'Event's
33+ , leftmost
34+ , mergeMap
35+ , mergeList
36+ , mergeWith
37+ -- ** Breaking up 'Event's
38+ , splitE
39+ , fanMap
40+ , dmapToThese
41+ , EitherTag (..)
42+ , eitherToDSum
43+ , dsumToEither
44+ -- ** Switching between 'Event's
45+ , switchPromptly
46+ -- ** Using 'Event's to sample 'Behavior's
47+ , tag
48+ , attach
49+ , attachWith
50+ , attachWithMaybe
51+ -- ** Blocking an 'Event' based on a 'Behavior'
52+ , gate
53+ -- ** Combining 'Dynamic's
54+ , distributeDMapOverDynPure
55+ , zipDyn
56+ , zipDynWith
57+ -- ** Accumulating state
58+ , Accumulator (..)
59+ , mapAccum_
60+ , mapAccumM_
61+ , mapAccumMaybeM_
62+ , zipListWithEvent
63+ , headE
64+ , tailE
65+ , switcher
66+ -- * Debugging functions
67+ , traceEvent
68+ , traceEventWith
69+ -- * Unsafe functions
70+ , unsafeDynamic
71+ -- * 'FunctorMaybe'
72+ , FunctorMaybe (..)
73+ , fforMaybe
74+ , ffilter
75+ -- * Miscellaneous convenience functions
76+ , ffor
77+ -- * Deprecated functions
78+ , appendEvents
79+ , onceE
80+ , sequenceThese
81+ ) where
3
82
4import Control.Applicative
83import Control.Applicative
5−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
84+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
85+import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
6−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
86+import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
7−import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
8−import Control.Monad.Trans.Writer (WriterT())
87+import Control.Monad.Trans.Cont (ContT ())
9−import Control.Monad.Trans.Except (ExceptT())
88+import Control.Monad.Trans.Except (ExceptT ())
10−import Control.Monad.Trans.Cont (ContT())
11−import Control.Monad.Trans.RWS (RWST())
89+import Control.Monad.Trans.RWS (RWST ())
12−import Data.List.NonEmpty (NonEmpty (..))
90+import Control.Monad.Trans.Writer (WriterT ())
13−import Data.These
14import Data.Align
91import Data.Align
15−import Data.GADT.Compare (GEq (..), (:~:) (..))
92+import Data.Bifunctor
16−import Data.GADT.Show (GShow (..))
17−import Data.Dependent.Sum (ShowTag (..))
18−import Data.Map (Map)
19import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
93import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
20import qualified Data.Dependent.Map as DMap
94import qualified Data.Dependent.Map as DMap
95+import Data.Dependent.Sum (ShowTag (..))
96+import Data.Foldable
97+import qualified Data.Functor.Bind as Bind
98+import Data.Functor.Bind hiding (join)
99+import Data.Functor.Compose
100+import Data.Functor.Constant
21import Data.Functor.Misc
101import Data.Functor.Misc
22−import Data.Semigroup
102+import Data.Functor.Plus
103+import Data.GADT.Compare ((:~:) (..), GEq (..))
104+import Data.GADT.Show (GShow (..))
105+import Data.List.NonEmpty (NonEmpty (..))
106+import Data.Map (Map)
107+import Data.Maybe
108+import Data.Monoid hiding (Alt, (<>))
109+import Data.Semigroup (Semigroup, sconcat, stimes, stimesIdempotentMonoid, (<>))
110+import Data.String
111+import Data.These
23import Data.Traversable
112import Data.Traversable
24
113
25-- Note: must come last to silence warnings due to AMP on GHC < 7.10
114-- Note: must come last to silence warnings due to AMP on GHC < 7.10
26−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
115+import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
27
116
28import Debug.Trace (trace)
117import Debug.Trace (trace)
29
118
30−class (MonadHold t (PushM t), MonadSample t (PullM t), MonadFix (PushM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
119+-- | The 'Reflex' class contains all the primitive functionality needed for
120+-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
121+-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
122+-- and the type of the timeline determines the FRP engine to be used. For most
123+-- purposes, the 'Reflex.Spider' implementation is recommended.
124+class ( MonadHold t (PushM t)
125+ , MonadSample t (PullM t)
126+ , MonadFix (PushM t)
127+ , Functor (Event t)
128+ , Functor (Behavior t)
129+ , Functor (Dynamic t)
130+ , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
131+ , Monad (Dynamic t)
132+ ) => Reflex t where
133+ -- | A container for a value that can change over time. 'Behavior's can be
31− -- | A container for a value that can change over time. Behaviors can be sampled at will, but it is not possible to be notified when they change
134+ -- sampled at will, but it is not possible to be notified when they change
32 data Behavior t :: * -> *
135 data Behavior t :: * -> *
136+ -- | A stream of occurrences. During any given frame, an 'Event' is either
33− -- | A stream of occurrences. During any given frame, an Event is either occurring or not occurring; if it is occurring, it will contain a value of the given type (its "occurrence type")
137+ -- occurring or not occurring; if it is occurring, it will contain a value of
138+ -- the given type (its "occurrence type")
34 data Event t :: * -> *
139 data Event t :: * -> *
140+ -- | A container for a value that can change over time and allows
141+ -- notifications on changes. Basically a combination of a 'Behavior' and an
142+ -- 'Event', with a rule that the 'Behavior' will change if and only if the
143+ -- 'Event' fires.
144+ data Dynamic t :: * -> *
145+ -- | A container for a value that can change over time and allows
146+ -- notifications on changes. Basically a combination of a 'Behavior' and an
147+ -- 'Event', with a rule that the 'Behavior' will change if and only if the
148+ -- 'Event' fires.
149+ data Incremental t :: (* -> *) -> * -> *
35− -- | An Event with no occurrences
150+ -- | An 'Event' with no occurrences
36 never :: Event t a
151 never :: Event t a
37− -- | Create a Behavior that always has the given value
152+ -- | Create a 'Behavior' that always has the given value
38− constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of Behaviors doesn't have a negative performance impact
153+ constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of 'Behavior's doesn't have a negative performance impact
39− -- | Create an Event from another Event; the provided function can sample Behaviors and hold Events, and use the results to produce a occurring (Just) or non-occurring (Nothing) result
154+ -- | Create an 'Event' from another 'Event'; the provided function can sample
155+ -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
156+ -- (Just) or non-occurring (Nothing) result
40 push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
157 push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
41 -- | A monad for doing complex push-based calculations efficiently
158 -- | A monad for doing complex push-based calculations efficiently
42 type PushM t :: * -> *
159 type PushM t :: * -> *
43− -- | Create a Behavior by reading from other Behaviors; the result will be recomputed whenever any of the read Behaviors changes
160+ -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
161+ -- recomputed whenever any of the read 'Behavior's changes
44 pull :: PullM t a -> Behavior t a
162 pull :: PullM t a -> Behavior t a
45 -- | A monad for doing complex pull-based calculations efficiently
163 -- | A monad for doing complex pull-based calculations efficiently
46 type PullM t :: * -> *
164 type PullM t :: * -> *
165+ -- | Merge a collection of events; the resulting 'Event' will only occur if at
47− -- | Merge a collection of events; the resulting Event will only occur if at least one input event is occuring, and will contain all of the input keys that are occurring simultaneously
166+ -- least one input event is occuring, and will contain all of the input keys
167+ -- that are occurring simultaneously
48 merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
168 merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
49− -- | Efficiently fan-out an event to many destinations. This function should be partially applied, and then the result applied repeatedly to create child events
169+ -- | Efficiently fan-out an event to many destinations. This function should
170+ -- be partially applied, and then the result applied repeatedly to create
171+ -- child events
50 fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
172 fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
51− -- | Create an Event that will occur whenever the currently-selected input Event occurs
173+ -- | Create an 'Event' that will occur whenever the currently-selected input
174+ -- 'Event' occurs
52 switch :: Behavior t (Event t a) -> Event t a
175 switch :: Behavior t (Event t a) -> Event t a
53− -- | Create an Event that will occur whenever the input event is occurring and its occurrence value, another Event, is also occurring
176+ -- | Create an 'Event' that will occur whenever the input event is occurring
177+ -- and its occurrence value, another 'Event', is also occurring
54 coincidence :: Event t (Event t a) -> Event t a
178 coincidence :: Event t (Event t a) -> Event t a
179+ -- | Extract the 'Behavior' of a 'Dynamic'.
180+ current :: Dynamic t a -> Behavior t a
181+ -- | Extract the 'Event' of the 'Dynamic'.
182+ updated :: Dynamic t a -> Event t a
183+ -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
184+ -- recent firing of the given 'Event', if any.
185+ unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
186+ -- | Create a new 'Incremental'. The given 'PullM''s value must always change
187+ -- in the same way that the accumulated application of patches would change
188+ -- that value.
189+ unsafeBuildIncremental :: Patch p => PullM t a -> Event t (p a) -> Incremental t p a
190+ -- | Create a merge whose parents can change over time
191+ mergeIncremental :: GCompare k => Incremental t PatchDMap (DMap k (Event t)) -> Event t (DMap k Identity)
192+ -- | Extract the 'Behavior' component of an 'Incremental'
193+ currentIncremental :: Patch p => Incremental t p a -> Behavior t a
194+ -- | Extract the 'Event' component of an 'Incremental'
195+ updatedIncremental :: Patch p => Incremental t p a -> Event t (p a)
196+ -- | Convert an 'Incremental' to a 'Dynamic'
197+ incrementalToDynamic :: Patch p => Incremental t p a -> Dynamic t a
198+
199+-- | A 'Patch' type represents a kind of change made to a datastructure.
200+class Patch p where
201+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
202+ -- 'Nothing'.
203+ apply :: p a -> a -> Maybe a
204+
205+instance Patch Identity where
206+ apply (Identity a) _ = Just a
207+
208+-- | A set of changes to a 'DMap'. Only DMap types are allowed for @a@.
209+data PatchDMap a where
210+ PatchDMap :: GCompare k => DMap k (Compose Maybe v) -> PatchDMap (DMap k v)
211+
212+instance GCompare k => Semigroup (PatchDMap (DMap k v)) where
213+ PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
214+ -- PatchDMap is idempotent, so stimes n is id for every n
215+#if MIN_VERSION_semigroups(0,17,0)
216+ stimes = stimesIdempotentMonoid
217+#else
218+ times1p n x = case compare n 0 of
219+ LT -> error "stimesIdempotentMonoid: negative multiplier"
220+ EQ -> mempty
221+ GT -> x
222+#endif
55
223
224+instance GCompare k => Monoid (PatchDMap (DMap k v)) where
225+ mempty = PatchDMap mempty
226+ mappend = (<>)
227+
228+instance Patch PatchDMap where
229+ 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?
230+ where insertions = DMap.mapMaybeWithKey (const $ getCompose) diff
231+ deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getCompose) diff
232+ nothingToJust = \case
233+ Nothing -> Just $ Constant ()
234+ Just _ -> Nothing
235+
236+-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
237+-- _must_ change when and only when the 'Event' fires, such that the
238+-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
239+-- if this is not the case, the resulting 'Dynamic' will behave
240+-- nondeterministically.
241+unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
242+unsafeDynamic = unsafeBuildDynamic . sample
243+
244+-- | Construct a 'Dynamic' value that never changes
245+constDyn :: Reflex t => a -> Dynamic t a
246+constDyn = pure
247+
248+-- | 'MonadSample' designates monads that can read the current value of a
249+-- 'Behavior'. This includes both 'PullM' and 'PushM'.
56class (Applicative m, Monad m) => MonadSample t m | m -> t where
250class (Applicative m, Monad m) => MonadSample t m | m -> t where
57− -- | Get the current value in the Behavior
251+ -- | Get the current value in the 'Behavior'
58 sample :: Behavior t a -> m a
252 sample :: Behavior t a -> m a
59
253
254+-- | 'MonadHold' designates monads that can create new 'Behavior's based on
255+-- 'Event's; usually this will be 'PushM' or a monad based on it. 'MonadHold'
256+-- is required to create any stateful computations with Reflex.
60class MonadSample t m => MonadHold t m where
257class MonadSample t m => MonadHold t m where
61− -- | Create a new Behavior whose value will initially be equal to the given value and will be updated whenever the given Event occurs. The update takes effect immediately after the Event occurs; if the occurrence that sets the Behavior (or one that is simultaneous with it) is used to sample the Behavior, it will see the *old* value of the Behavior, not the new one.
258+ -- | Create a new 'Behavior' whose value will initially be equal to the given
259+ -- value and will be updated whenever the given 'Event' occurs. The update
260+ -- takes effect immediately after the 'Event' occurs; if the occurrence that
261+ -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
262+ -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
263+ -- one.
62 hold :: a -> Event t a -> m (Behavior t a)
264 hold :: a -> Event t a -> m (Behavior t a)
63−
265+ -- | Create a 'Dynamic' value using the given initial value that changes every
266+ -- time the 'Event' occurs.
267+ holdDyn :: a -> Event t a -> m (Dynamic t a)
268+ -- | Create an 'Incremental' value using the given initial value that changes
269+ -- every time the 'Event' occurs.
270+ holdIncremental :: Patch p => a -> Event t (p a) -> m (Incremental t p a)
271+
272+-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
273+-- key. This is much more efficient than filtering for each key with
274+-- 'fmapMaybe'.
64−newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
275+newtype EventSelector t k = EventSelector
276+ { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
277+ -- determined by the type of the key, so this can be used to fan-out
278+ -- 'Event's whose sub-'Event's have different types.
279+ --
280+ -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
281+ -- (but equivalent to) using 'fmapMaybe' to select only the relevant
282+ -- occurrences of an 'Event'.
283+ select :: forall a. k a -> Event t a
284+ }
65
285
66--------------------------------------------------------------------------------
286--------------------------------------------------------------------------------
67-- Instances
287-- Instances
@@ -72,85 +292,123 @@ instance MonadSample t m => MonadSample t (ReaderT r m) where
72
292
73instance MonadHold t m => MonadHold t (ReaderT r m) where
293instance MonadHold t m => MonadHold t (ReaderT r m) where
74 hold a0 = lift . hold a0
294 hold a0 = lift . hold a0
295+ holdDyn a0 = lift . holdDyn a0
296+ holdIncremental a0 = lift . holdIncremental a0
75
297
76instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
298instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
77 sample = lift . sample
299 sample = lift . sample
78
300
79instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
301instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
80 hold a0 = lift . hold a0
302 hold a0 = lift . hold a0
303+ holdDyn a0 = lift . holdDyn a0
304+ holdIncremental a0 = lift . holdIncremental a0
81
305
82instance MonadSample t m => MonadSample t (StateT s m) where
306instance MonadSample t m => MonadSample t (StateT s m) where
83 sample = lift . sample
307 sample = lift . sample
84
308
85instance MonadHold t m => MonadHold t (StateT s m) where
309instance MonadHold t m => MonadHold t (StateT s m) where
86 hold a0 = lift . hold a0
310 hold a0 = lift . hold a0
311+ holdDyn a0 = lift . holdDyn a0
312+ holdIncremental a0 = lift . holdIncremental a0
87
313
88instance MonadSample t m => MonadSample t (ExceptT e m) where
314instance MonadSample t m => MonadSample t (ExceptT e m) where
89 sample = lift . sample
315 sample = lift . sample
90
316
91instance MonadHold t m => MonadHold t (ExceptT e m) where
317instance MonadHold t m => MonadHold t (ExceptT e m) where
92 hold a0 = lift . hold a0
318 hold a0 = lift . hold a0
319+ holdDyn a0 = lift . holdDyn a0
320+ holdIncremental a0 = lift . holdIncremental a0
93
321
94instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
322instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
95 sample = lift . sample
323 sample = lift . sample
96
324
97instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
325instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
98 hold a0 = lift . hold a0
326 hold a0 = lift . hold a0
327+ holdDyn a0 = lift . holdDyn a0
328+ holdIncremental a0 = lift . holdIncremental a0
99
329
100instance MonadSample t m => MonadSample t (ContT r m) where
330instance MonadSample t m => MonadSample t (ContT r m) where
101 sample = lift . sample
331 sample = lift . sample
102
332
103instance MonadHold t m => MonadHold t (ContT r m) where
333instance MonadHold t m => MonadHold t (ContT r m) where
104 hold a0 = lift . hold a0
334 hold a0 = lift . hold a0
335+ holdDyn a0 = lift . holdDyn a0
336+ holdIncremental a0 = lift . holdIncremental a0
105
337
106--------------------------------------------------------------------------------
338--------------------------------------------------------------------------------
107-- Convenience functions
339-- Convenience functions
108--------------------------------------------------------------------------------
340--------------------------------------------------------------------------------
109
341
110−-- | Create an Event from another Event.
342+-- | Create an 'Event' from another 'Event'. The provided function can sample
111−-- The provided function can sample 'Behavior's and hold 'Event's.
343+-- 'Behavior's and hold 'Event's.
112pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
344pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
113−pushAlways f = push (liftM Just . f)
345+pushAlways f = push (fmap Just . f)
114
346
115-- | Flipped version of 'fmap'.
347-- | Flipped version of 'fmap'.
116ffor :: Functor f => f a -> (a -> b) -> f b
348ffor :: Functor f => f a -> (a -> b) -> f b
117ffor = flip fmap
349ffor = flip fmap
118
350
119−instance Reflex t => Functor (Behavior t) where
120− fmap f = pull . liftM f . sample
121−
122instance Reflex t => Applicative (Behavior t) where
351instance Reflex t => Applicative (Behavior t) where
123 pure = constant
352 pure = constant
124 f <*> x = pull $ sample f `ap` sample x
353 f <*> x = pull $ sample f `ap` sample x
125 _ *> b = b
354 _ *> b = b
126 a <* _ = a
355 a <* _ = a
127
356
357+instance Reflex t => Apply (Behavior t) where
358+ (<.>) = (<*>)
359+
360+instance Reflex t => Bind (Behavior t) where
361+ (>>-) = (>>=)
362+
363+instance (Reflex t, Fractional a) => Fractional (Behavior t a) where
364+ (/) = liftA2 (/)
365+ fromRational = pure . fromRational
366+ recip = fmap recip
367+
368+instance Reflex t => Functor (Behavior t) where
369+ fmap f = pull . fmap f . sample
370+
371+instance (Reflex t, IsString a) => IsString (Behavior t a) where
372+ fromString = pure . fromString
373+
128instance Reflex t => Monad (Behavior t) where
374instance Reflex t => Monad (Behavior t) where
129 a >>= f = pull $ sample a >>= sample . f
375 a >>= f = pull $ sample a >>= sample . f
130− -- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine Behaviors other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
376+ -- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine 'Behavior's other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
131 return = constant
377 return = constant
132 fail = error "Monad (Behavior t) does not support fail"
378 fail = error "Monad (Behavior t) does not support fail"
133
379
380+instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
381+ mempty = constant mempty
382+ mappend a b = pull $ liftM2 mappend (sample a) (sample b)
383+ mconcat = pull . fmap mconcat . mapM sample
384+
385+instance (Reflex t, Num a) => Num (Behavior t a) where
386+ (+) = liftA2 (+)
387+ (-) = liftA2 (-)
388+ (*) = liftA2 (*)
389+ abs = fmap abs
390+ fromInteger = pure . fromInteger
391+ negate = fmap negate
392+ signum = fmap signum
393+
134instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
394instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
135 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
395 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
136− sconcat = pull . liftM sconcat . mapM sample
396+ sconcat = pull . fmap sconcat . mapM sample
137#if MIN_VERSION_semigroups(0,17,0)
397#if MIN_VERSION_semigroups(0,17,0)
138 stimes n = fmap $ stimes n
398 stimes n = fmap $ stimes n
139#else
399#else
140 times1p n = fmap $ times1p n
400 times1p n = fmap $ times1p n
141#endif
401#endif
142
402
143−instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
144− mempty = constant mempty
145− mappend a b = pull $ liftM2 mappend (sample a) (sample b)
146− mconcat = pull . liftM mconcat . mapM sample
147−
148--TODO: See if there's a better class in the standard libraries already
403--TODO: See if there's a better class in the standard libraries already
149-- | A class for values that combines filtering and mapping using 'Maybe'.
404-- | A class for values that combines filtering and mapping using 'Maybe'.
150class FunctorMaybe f where
405class FunctorMaybe f where
151 -- | Combined mapping and filtering function.
406 -- | Combined mapping and filtering function.
152 fmapMaybe :: (a -> Maybe b) -> f a -> f b
407 fmapMaybe :: (a -> Maybe b) -> f a -> f b
153
408
409+instance FunctorMaybe [] where
410+ fmapMaybe f = catMaybes . fmap f
411+
154-- | Flipped version of 'fmapMaybe'.
412-- | Flipped version of 'fmapMaybe'.
155fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
413fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
156fforMaybe = flip fmapMaybe
414fforMaybe = flip fmapMaybe
@@ -160,53 +418,52 @@ fforMaybe = flip fmapMaybe
160ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
418ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
161ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
419ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
162
420
421+-- | Left-biased event union (prefers left event on simultaneous
422+-- occurrence).
163−instance Reflex t => FunctorMaybe (Event t) where
423+instance Reflex t => Alt (Event t) where
164− fmapMaybe f = push $ return . f
424+ ev1 <!> ev2 = leftmost [ev1, ev2]
425+
426+-- | 'Event' intersection (convenient interface to 'coincidence').
427+instance Reflex t => Apply (Event t) where
428+ evf <.> evx = coincidence (fmap (<$> evx) evf)
429+
430+-- | 'Event' intersection (convenient interface to 'coincidence').
431+instance Reflex t => Bind (Event t) where
432+ evx >>- f = coincidence (f <$> evx)
433+ join = coincidence
165
434
166instance Reflex t => Functor (Event t) where
435instance Reflex t => Functor (Event t) where
167 fmap f = fmapMaybe $ Just . f
436 fmap f = fmapMaybe $ Just . f
168
437
169−-- | Create a new 'Event' by combining each occurence with the next value
438+instance Reflex t => FunctorMaybe (Event t) where
170−-- of the list using the supplied function. If the list runs out of items,
439+ fmapMaybe f = push $ return . f
171−-- all subsequent 'Event' occurrences will be ignored.
440+
172−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
441+-- | Never: @'zero' = 'never'@.
173−zipListWithEvent f l e = do
442+instance Reflex t => Plus (Event t) where
174− rec lb <- hold l eTail
443+ zero = never
175− let eBoth = flip push e $ \o -> do
176− l' <- sample lb
177− return $ case l' of
178− (h : t) -> Just (f h o, t)
179− [] -> Nothing
180− let eTail = fmap snd eBoth
181− lb `seq` eBoth `seq` eTail `seq` return ()
182− return $ fmap fst eBoth
183
444
184-- | Replace each occurrence value of the 'Event' with the value of the
445-- | Replace each occurrence value of the 'Event' with the value of the
185-- 'Behavior' at the time of that occurrence.
446-- 'Behavior' at the time of that occurrence.
186tag :: Reflex t => Behavior t b -> Event t a -> Event t b
447tag :: Reflex t => Behavior t b -> Event t a -> Event t b
187tag b = pushAlways $ \_ -> sample b
448tag b = pushAlways $ \_ -> sample b
188
449
189−-- | Create a new 'Event' that combines occurences of supplied 'Event'
450+-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
190−-- with the current value of the 'Behavior'.
451+-- current value of the 'Behavior'.
191attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
452attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
192attach = attachWith (,)
453attach = attachWith (,)
193
454
194−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
455+-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
195−-- by combining it with the current value of the 'Behavior'.
456+-- combining it with the current value of the 'Behavior'.
196attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
457attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
197attachWith f = attachWithMaybe $ \a b -> Just $ f a b
458attachWith f = attachWithMaybe $ \a b -> Just $ f a b
198
459
199−-- | Create a new 'Event' by combining each occurence with the current
460+-- | Create a new 'Event' by combining each occurence with the current value of
200−-- value of the 'Behavior'. The occurrence is discarded if the combining function
461+-- the 'Behavior'. The occurrence is discarded if the combining function returns
201−-- returns Nothing
462+-- Nothing
202attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
463attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
203−attachWithMaybe f b e = flip push e $ \o -> liftM (flip f o) $ sample b
464+attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
204
465
205−-- | Alias for 'headE'
206−onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
207−onceE = headE
208−
209−-- | Create a new 'Event' that only occurs on the first occurence of
466+-- | Create a new 'Event' that only occurs only once, on the first occurence of
210-- the supplied 'Event'.
467-- the supplied 'Event'.
211headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
468headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
212headE e = do
469headE e = do
@@ -215,38 +472,38 @@ headE e = do
215 e' `seq` return ()
472 e' `seq` return ()
216 return e'
473 return e'
217
474
218−-- | Create a new 'Event' that occurs on all but the first occurence
475+-- | Create a new 'Event' that occurs on all but the first occurence of the
219−-- of the supplied 'Event'.
476+-- supplied 'Event'.
220tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
477tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
221−tailE e = liftM snd $ headTailE e
478+tailE e = snd <$> headTailE e
222
479
223−-- | Create a tuple of two 'Event's with the first one occuring only
480+-- | Create a tuple of two 'Event's with the first one occuring only the first
224−-- the first time the supplied 'Event' occurs and the second occuring
481+-- time the supplied 'Event' occurs and the second occuring on all but the first
225−-- on all but the first occurence.
482+-- occurence.
226headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
483headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
227headTailE e = do
484headTailE e = do
228 eHead <- headE e
485 eHead <- headE e
229 be <- hold never $ fmap (const e) eHead
486 be <- hold never $ fmap (const e) eHead
230 return (eHead, switch be)
487 return (eHead, switch be)
231
488
232−-- | Split the supplied 'Event' into two individual 'Event's occuring
489+-- | Split the supplied 'Event' into two individual 'Event's occuring at the
233−-- at the same time with the respective values from the tuple.
490+-- same time with the respective values from the tuple.
234splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
491splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
235splitE e = (fmap fst e, fmap snd e)
492splitE e = (fmap fst e, fmap snd e)
236
493
237-- | Print the supplied 'String' and the value of the 'Event' on each
494-- | Print the supplied 'String' and the value of the 'Event' on each
238-- occurence. This should /only/ be used for debugging.
495-- occurence. This should /only/ be used for debugging.
239--
496--
240−-- Note: As with Debug.Trace.trace, the message will only be printed if
497+-- Note: As with Debug.Trace.trace, the message will only be printed if the
241−-- the 'Event' is actually used.
498+-- 'Event' is actually used.
242traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
499traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
243traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
500traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
244
501
245−-- | Print the output of the supplied function on each occurence of
502+-- | Print the output of the supplied function on each occurence of the
246−-- the 'Event'. This should /only/ be used for debugging.
503+-- 'Event'. This should /only/ be used for debugging.
247--
504--
248−-- Note: As with Debug.Trace.trace, the message will only be printed if
505+-- Note: As with Debug.Trace.trace, the message will only be printed if the
249−-- the 'Event' is actually used.
506+-- 'Event' is actually used.
250traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
507traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
251traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
508traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
252
509
@@ -298,59 +555,56 @@ dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
298 (Nothing, Just (Identity b)) -> Just $ That b
555 (Nothing, Just (Identity b)) -> Just $ That b
299 (Just (Identity a), Just (Identity b)) -> Just $ These a b
556 (Just (Identity a), Just (Identity b)) -> Just $ These a b
300
557
301−-- | Create a new 'Event' that occurs if at least one of the supplied
558+instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
302−-- 'Event's occurs. If both occur at the same time they are combined
559+ (<>) = alignWith (mergeThese (<>))
303−-- using 'mappend'.
560+ sconcat = fmap sconcat . mergeList . toList
304−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
561+#if MIN_VERSION_semigroups(0,17,0)
305−appendEvents e1 e2 = mergeThese mappend <$> align e1 e2
562+ stimes n = fmap $ stimes n
306−
563+#else
307−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
564+ times1p n = fmap $ times1p n
308−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
565+#endif
309−sequenceThese t = case t of
310− This ma -> liftM This ma
311− These ma mb -> liftM2 These ma mb
312− That mb -> liftM That mb
313
566
314instance (Semigroup a, Reflex t) => Monoid (Event t a) where
567instance (Semigroup a, Reflex t) => Monoid (Event t a) where
315 mempty = never
568 mempty = never
316− mappend a b = mconcat [a, b]
569+ mappend = (<>)
317 mconcat = fmap sconcat . mergeList
570 mconcat = fmap sconcat . mergeList
318
571
319−-- | Create a new 'Event' that occurs if at least one of the 'Event's
572+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
320−-- in the list occurs. If multiple occur at the same time they are
573+-- list occurs. If multiple occur at the same time they are folded from the left
321−-- folded from the left with the given function.
574+-- with the given function.
322mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
575mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
323mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
576mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
324 . merge
577 . merge
325 . DMap.fromDistinctAscList
578 . DMap.fromDistinctAscList
326− . map (\(k, v) -> (Const2 k) :=> v)
579+ . map (\(k, v) -> Const2 k :=> v)
327 $ zip [0 :: Int ..] es
580 $ zip [0 :: Int ..] es
328
581
329−-- | Create a new 'Event' that occurs if at least one of the 'Event's
582+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
330−-- in the list occurs. If multiple occur at the same time the value is
583+-- list occurs. If multiple occur at the same time the value is the value of the
331−-- the value of the leftmost event.
584+-- leftmost event.
332leftmost :: Reflex t => [Event t a] -> Event t a
585leftmost :: Reflex t => [Event t a] -> Event t a
333leftmost = mergeWith const
586leftmost = mergeWith const
334
587
335−-- | Create a new 'Event' that occurs if at least one of the 'Event's
588+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
336−-- in the list occurs and has a list of the values of all 'Event's
589+-- list occurs and has a list of the values of all 'Event's occuring at that
337−-- occuring at that time.
590+-- time.
338mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
591mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
339mergeList [] = never
592mergeList [] = never
340mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
593mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
341
594
342−-- | Create a new 'Event' combining the map of 'Event's into an
595+-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
343−-- 'Event' that occurs if at least one of them occurs and has a map of
596+-- occurs if at least one of them occurs and has a map of values of all 'Event's
344−-- values of all 'Event's occuring at that time.
597+-- occuring at that time.
345mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
598mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
346mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
599mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
347
600
348−-- | Split the event into an 'EventSelector' that allows efficient
601+-- | Split the event into an 'EventSelector' that allows efficient selection of
349−-- selection of the individual 'Event's.
602+-- the individual 'Event's.
350fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
603fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
351fanMap = fan . fmap mapToDMap
604fanMap = fan . fmap mapToDMap
352
605
353−-- | Switches to the new event whenever it receives one; the new event is used immediately, on the same frame that it is switched to
606+-- | Switches to the new event whenever it receives one; the new event is used
607+-- immediately, on the same frame that it is switched to
354switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
608switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
355switchPromptly ea0 eea = do
609switchPromptly ea0 eea = do
356 bea <- hold ea0 eea
610 bea <- hold ea0 eea
@@ -362,13 +616,164 @@ instance Reflex t => Align (Event t) where
362 nil = never
616 nil = never
363 align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
617 align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
364
618
365−-- | Create a new 'Event' that only occurs if the supplied 'Event'
619+-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
366−-- occurs and the 'Behavior' is true at the time of occurence.
620+-- the 'Behavior' is true at the time of occurence.
367gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
621gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
368gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
622gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
369
623
370−-- | Create a new behavior given a starting behavior and switch to a the
624+-- | Create a new behavior given a starting behavior and switch to a the behvior
371−-- behvior carried by the event when it fires.
625+-- carried by the event when it fires.
372switcher :: (Reflex t, MonadHold t m)
626switcher :: (Reflex t, MonadHold t m)
373 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
627 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
374switcher b eb = pull . (sample <=< sample) <$> hold b eb
628switcher b eb = pull . (sample <=< sample) <$> hold b eb
629+
630+-- | Combine two 'Dynamic's. The result will change whenever either (or both)
631+-- input 'Dynamic' changes. Equivalent to @zipDynWith (,)@.
632+zipDyn :: Reflex t => Dynamic t a -> Dynamic t b -> Dynamic t (a, b)
633+zipDyn = zipDynWith (,)
634+
635+-- | Combine two 'Dynamic's with a combining function. The result will change
636+-- whenever either (or both) input 'Dynamic' changes.
637+zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
638+zipDynWith f da db =
639+ let eab = align (updated da) (updated db)
640+ ec = flip push eab $ \o -> do
641+ (a, b) <- case o of
642+ This a -> do
643+ b <- sample $ current db
644+ return (a, b)
645+ That b -> do
646+ a <- sample $ current da
647+ return (a, b)
648+ These a b -> return (a, b)
649+ return $ Just $ f a b
650+ in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
651+
652+instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
653+ mconcat = fmap (mconcat . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
654+ mempty = constDyn mempty
655+ mappend = zipDynWith mappend
656+
657+-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
658+-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
659+-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
660+distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
661+distributeDMapOverDynPure dm = case DMap.toList dm of
662+ [] -> constDyn DMap.empty
663+ [k :=> v] -> fmap (DMap.singleton k . Identity) v
664+ _ ->
665+ let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
666+ edmPre = merge $ DMap.map updated dm
667+ result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
668+ olds <- sample $ current result
669+ return $ DMap.unionWithKey (\_ _ new -> new) olds news
670+ in result
671+
672+--------------------------------------------------------------------------------
673+-- Accumulator
674+--------------------------------------------------------------------------------
675+
676+-- | An 'Accumulator' type can be built by accumulating occurrences of an
677+-- 'Event'.
678+class Reflex t => Accumulator t f | f -> t where
679+ accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
680+ accum f = accumMaybe $ \v o -> Just $ f v o
681+ accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
682+ accumM f = accumMaybeM $ \v o -> Just <$> f v o
683+ accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
684+ accumMaybe f = accumMaybeM $ \v o -> return $ f v o
685+ accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
686+ mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
687+ mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
688+ mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
689+ mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
690+ mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
691+ mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
692+ mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
693+
694+-- | Accumulate occurrences of an 'Event', producing an output occurrence each
695+-- time. Discard the underlying 'Accumulator'.
696+mapAccum_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Event t c)
697+mapAccum_ f z e = do
698+ (_ :: Behavior t a, result) <- mapAccum f z e
699+ return result
700+
701+-- | Accumulate occurrences of an 'Event', possibly producing an output
702+-- occurrence each time. Discard the underlying 'Accumulator'.
703+mapAccumMaybe_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
704+mapAccumMaybe_ f z e = do
705+ (_ :: Behavior t a, result) <- mapAccumMaybe f z e
706+ return result
707+
708+-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
709+-- an output occurrence each time. Discard the underlying 'Accumulator'.
710+mapAccumM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Event t c)
711+mapAccumM_ f z e = do
712+ (_ :: Behavior t a, result) <- mapAccumM f z e
713+ return result
714+
715+-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
716+-- producing an output occurrence each time. Discard the underlying
717+-- 'Accumulator'.
718+mapAccumMaybeM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
719+mapAccumMaybeM_ f z e = do
720+ (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
721+ return result
722+
723+instance Reflex t => Accumulator t (Dynamic t) where
724+ accumMaybeM f z e = do
725+ rec let e' = flip push e $ \o -> do
726+ v <- sample $ current d'
727+ f v o
728+ d' <- holdDyn z e'
729+ return d'
730+ mapAccumMaybeM f z e = do
731+ rec let e' = flip push e $ \o -> do
732+ v <- sample $ current d'
733+ result <- f v o
734+ return $ case result of
735+ (Nothing, Nothing) -> Nothing
736+ _ -> Just result
737+ d' <- holdDyn z $ fmapMaybe fst e'
738+ return (d', fmapMaybe snd e')
739+
740+instance Reflex t => Accumulator t (Behavior t) where
741+ accumMaybeM f z e = current <$> accumMaybeM f z e
742+ mapAccumMaybeM f z e = first current <$> mapAccumMaybeM f z e
743+
744+instance Reflex t => Accumulator t (Event t) where
745+ accumMaybeM f z e = updated <$> accumMaybeM f z e
746+ mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
747+
748+-- | Create a new 'Event' by combining each occurence with the next value of the
749+-- list using the supplied function. If the list runs out of items, all
750+-- subsequent 'Event' occurrences will be ignored.
751+zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
752+zipListWithEvent f l e = do
753+ let f' a b = case a of
754+ h:t -> (Just t, Just $ f h b)
755+ _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
756+ mapAccumMaybe_ f' l e
757+
758+--------------------------------------------------------------------------------
759+-- Deprecated functions
760+--------------------------------------------------------------------------------
761+
762+-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
763+-- occurs. If both occur at the same time they are combined using 'mappend'.
764+{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
765+appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
766+appendEvents = alignWith $ mergeThese mappend
767+
768+-- | Alias for 'headE'
769+{-# DEPRECATED onceE "Use 'headE' instead" #-}
770+onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
771+onceE = headE
772+
773+{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
774+{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
775+sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
776+sequenceThese t = case t of
777+ This ma -> liftM This ma
778+ These ma mb -> liftM2 These ma mb
779+ That mb -> liftM That mb
src/Reflex/Dynamic.hs+335−332
@@ -1,187 +1,130 @@
1−{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, DataKinds, PolyKinds #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE NoMonomorphismRestriction #-}
9+{-# LANGUAGE PolyKinds #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE TypeOperators #-}
14+{-# LANGUAGE UndecidableInstances #-}
15+-- | This module contains various functions for working with 'Dynamic' values.
16+-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
17+module Reflex.Dynamic
18+ ( -- * Basics
2−module Reflex.Dynamic ( Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
19+ Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
3− , current
20+ , current
4− , updated
21+ , updated
5− , constDyn
6− , holdDyn
22+ , holdDyn
7− , nubDyn
23+ , constDyn
24+ , uniqDyn
25+ , uniqDynBy
8− , count
26+ , count
9− , toggle
27+ , toggle
10− , switchPromptlyDyn
28+ , switchPromptlyDyn
11− , tagDyn
29+ , tagPromptlyDyn
12− , attachDyn
30+ , attachPromptlyDyn
13− , attachDynWith
31+ , attachPromptlyDynWith
14− , attachDynWithMaybe
32+ , attachPromptlyDynWithMaybe
15− , mapDyn
16− , forDyn
17− , mapDynM
18− , foldDyn
33+ , foldDyn
19− , foldDynM
34+ , foldDynM
20− , foldDynMaybe
35+ , foldDynMaybe
21− , foldDynMaybeM
36+ , foldDynMaybeM
22− , combineDyn
23− , apDyn
24− , collectDyn
25− , mconcatDyn
26− , distributeDMapOverDyn
27− , joinDyn
28− , joinDynThroughMap
37+ , joinDynThroughMap
29− , traceDyn
38+ , traceDyn
30− , traceDynWith
39+ , traceDynWith
31− , splitDyn
40+ , splitDynPure
41+ , distributeDMapOverDynPure
32− , Demux
42+ , Demux
33− , demux
43+ , demux
34− , getDemuxed
44+ , demuxed
35− -- Things that probably aren't very useful:
45+ -- Things that probably aren't very useful:
36− , HList (..)
46+ , HList (..)
37− , FHList (..)
47+ , FHList (..)
38− , distributeFHListOverDyn
48+ , collectDynPure
49+ , distributeFHListOverDynPure
39− -- Unsafe
50+ -- Unsafe
40− , unsafeDynamic
51+ , unsafeDynamic
52+ -- * Deprecated functions
53+ , apDyn
54+ , attachDyn
55+ , attachDynWith
56+ , attachDynWithMaybe
57+ , collectDyn
58+ , combineDyn
59+ , distributeDMapOverDyn
60+ , distributeFHListOverDyn
61+ , forDyn
62+ , getDemuxed
63+ , joinDyn
64+ , mapDyn
65+ , mconcatDyn
66+ , nubDyn
67+ , splitDyn
68+ , tagDyn
41− ) where
69+ ) where
42
70
43import Prelude hiding (mapM, mapM_)
71import Prelude hiding (mapM, mapM_)
44
72
45−import Reflex.Class
46import Data.Functor.Misc
73import Data.Functor.Misc
74+import Reflex.Class
47
75
76+import Control.Applicative ((<*>))
48−import Control.Monad hiding (mapM, mapM_, forM, forM_)
77+import Control.Monad hiding (forM, forM_, mapM, mapM_)
49import Control.Monad.Fix
78import Control.Monad.Fix
50−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_)
79+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
51−import Data.These
52−import Data.Traversable (mapM, forM)
53import Data.Align
80import Data.Align
54−import Data.Map (Map)
55−import qualified Data.Map as Map
56import Data.Dependent.Map (DMap)
81import Data.Dependent.Map (DMap)
57import qualified Data.Dependent.Map as DMap
82import qualified Data.Dependent.Map as DMap
58import Data.Dependent.Sum (DSum (..))
83import Data.Dependent.Sum (DSum (..))
59−import Data.GADT.Compare (GCompare (..), GEq (..), (:~:) (..), GOrdering (..))
84+import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
85+import Data.Map (Map)
60import Data.Monoid
86import Data.Monoid
61−--import Data.HList (HList (..), hBuild)
87+import Data.These
62−
63−data HList (l::[*]) where
64− HNil :: HList '[]
65− HCons :: e -> HList l -> HList (e ': l)
66−
67−infixr 2 `HCons`
68−
69−type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
70−type instance HRevApp '[] l = l
71−type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
72−
73−hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
74−hRevApp HNil l = l
75−hRevApp (HCons x l) l' = hRevApp l (HCons x l')
76−
77−hReverse :: HList l -> HList (HRevApp l '[])
78−hReverse l = hRevApp l HNil
79−
80−hBuild :: (HBuild' '[] r) => r
81−hBuild = hBuild' HNil
82−
83−class HBuild' l r where
84− hBuild' :: HList l -> r
85−
86−instance (l' ~ HRevApp l '[])
87− => HBuild' l (HList l') where
88− hBuild' l = hReverse l
89
88
90−instance HBuild' (a ': l) r
89+import Debug.Trace
91− => HBuild' l (a->r) where
92− hBuild' l x = hBuild' (HCons x l)
93
90
94−-- | A container for a value that can change over time and allows notifications on changes.
95−-- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
96−-- change if and only if the Event fires.
97−--
98−-- Although @Dynamic@ logically has a 'Functor' instance, currently it can\'t
99−-- be implemented without potentially requiring the mapped function to be
100−-- evaluated twice. Instead, you can use 'mapDyn', which runs in a 'MonadHold'
101−-- instance, to achieve the same result.
102−data Dynamic t a
103− = Dynamic (Behavior t a) (Event t a)
104−
105−unsafeDynamic :: Behavior t a -> Event t a -> Dynamic t a
106−unsafeDynamic = Dynamic
107−
108−-- | Extract the 'Behavior' of a 'Dynamic'.
109−current :: Dynamic t a -> Behavior t a
110−current (Dynamic b _) = b
111−
112−-- | Extract the 'Event' of the 'Dynamic'.
113−updated :: Dynamic t a -> Event t a
114−updated (Dynamic _ e) = e
115−
116−-- | 'Dynamic' with the constant supplied value.
117−constDyn :: Reflex t => a -> Dynamic t a
118−constDyn x = Dynamic (constant x) never
119−
120−-- | Create a 'Dynamic' using the initial value that changes every
121−-- time the 'Event' occurs.
122−holdDyn :: MonadHold t m => a -> Event t a -> m (Dynamic t a)
123−holdDyn v0 e = do
124− b <- hold v0 e
125− return $ Dynamic b e
126−
127−-- | Create a new 'Dynamic' that only signals changes if the values
91+-- | Create a new 'Dynamic' that only signals changes if the values actually
128−-- actually changed.
92+-- changed.
129−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
93+uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
130−nubDyn d =
94+uniqDyn = uniqDynBy (==)
131− let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
132− in Dynamic (current d) e' --TODO: Avoid invalidating the outgoing Behavior
133−
134−{-
135−instance Reflex t => Functor (Dynamic t) where
136− fmap f d =
137− let e' = fmap f $ updated d
138− eb' = push (\b' -> liftM Just $ constant b') e'
139− b0 = fmap f $ current d
140−
141−-}
142
95
143−-- | Map a function over a 'Dynamic'.
96+-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
97+-- changes and the given function returns 'False' when given both the old and
98+-- the new values.
144−mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
99+uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
145−mapDyn f = mapDynM $ return . f
100+uniqDynBy eq d =
101+ let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
102+ in unsafeDynamic (current d) e'
146
103
147−-- | Flipped version of 'mapDyn'.
148−forDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
149−forDyn = flip mapDyn
150−
151−-- | Map a monadic function over a 'Dynamic'. The only monadic action that the given function can
152−-- perform is 'sample'.
153−{-# INLINE mapDynM #-}
154−mapDynM :: forall t m a b. (Reflex t, MonadHold t m) => (forall m'. MonadSample t m' => a -> m' b) -> Dynamic t a -> m (Dynamic t b)
155−mapDynM f d = do
156− let e' = push (liftM Just . f :: a -> PushM t (Maybe b)) $ updated d
157− eb' = fmap constant e'
158− v0 = pull $ f =<< sample (current d)
159− bb' :: Behavior t (Behavior t b) <- hold v0 eb'
160− let b' = pull $ sample =<< sample bb'
161− return $ Dynamic b' e'
162−
163−-- | Create a 'Dynamic' using the initial value and change it each
104+-- | Create a 'Dynamic' using the initial value and change it each time the
164−-- time the 'Event' occurs using a folding function on the previous
105+-- 'Event' occurs using a folding function on the previous value and the value
165−-- value and the value of the 'Event'.
106+-- of the 'Event'.
166foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
107foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
167−foldDyn f = foldDynMaybe $ \o v -> Just $ f o v
108+foldDyn = accum . flip
168
109
169−-- | Create a 'Dynamic' using the initial value and change it each
110+-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
170−-- time the 'Event' occurs using a monadic folding function on the
111+-- can 'sample' existing 'Behaviors' and 'hold' new ones.
171−-- previous value and the value of the 'Event'.
172foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
112foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
173−foldDynM f = foldDynMaybeM $ \o v -> liftM Just $ f o v
113+foldDynM = accumM . flip
174−
114+
115+-- | Create a 'Dynamic' using the provided initial value and change it each time
116+-- the provided 'Event' occurs, using a function to combine the old value with
117+-- the 'Event''s value. If the function returns 'Nothing', the value is not
118+-- changed; this is distinct from returning 'Just' the old value, since the
119+-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
120+-- in the 'Nothing' case.
175foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
121foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
176−foldDynMaybe f = foldDynMaybeM $ \o v -> return $ f o v
122+foldDynMaybe = accumMaybe . flip
177
123
124+-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
125+-- can 'sample' existing 'Behaviors' and 'hold' new ones.
178foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
126foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
179−foldDynMaybeM f z e = do
127+foldDynMaybeM = accumMaybeM . flip
180− rec let e' = flip push e $ \o -> do
181− v <- sample b'
182− f o v
183− b' <- hold z e'
184− return $ Dynamic b' e'
185
128
186-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
129-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
187count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
130count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
@@ -192,136 +135,35 @@ count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
192toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
135toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
193toggle = foldDyn (const not)
136toggle = foldDyn (const not)
194
137
195−-- | Switches to the new 'Event' whenever it receives one. Switching
138+-- | Switches to the new 'Event' whenever it receives one. Switching occurs
196−-- occurs *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the old and new
139+-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
197−-- inner Events fire simultaneously, the output will fire with the value of the *new* 'Event'.
140+-- old and new inner Events fire simultaneously, the output will fire with the
141+-- value of the *new* 'Event'.
198switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
142switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
199switchPromptlyDyn de =
143switchPromptlyDyn de =
200 let eLag = switch $ current de
144 let eLag = switch $ current de
201 eCoincidences = coincidence $ updated de
145 eCoincidences = coincidence $ updated de
202 in leftmost [eCoincidences, eLag]
146 in leftmost [eCoincidences, eLag]
203
147
204−{-
148+-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
205−
149+splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
206−mergeEventsWith :: Reflex t m => (a -> a -> a) -> Event t a -> Event t a -> m (Event t a)
150+splitDynPure d = (fmap fst d, fmap snd d)
207−mergeEventsWith f ea eb = mapE (mergeThese f) =<< alignEvents ea eb
208−
209−firstE :: (Reflex t m) => [Event t a] -> m (Event t a)
210−firstE [] = return never
211−firstE (h:t) = mergeEventsLeftBiased h =<< firstE t
212−
213−concatEventsWith :: (Reflex t m) => (a -> a -> a) -> [Event t a] -> m (Event t a)
214−concatEventsWith _ [] = return never
215−concatEventsWith _ [e] = return e
216−concatEventsWith f es = mapEM (liftM (foldl1 f . map (\(Const2 _ :=> v) -> v) . DMap.toList) . sequenceDmap) <=< mergeEventDMap $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
217−--concatEventsWith f (h:t) = mergeEventsWith f h =<< concatEventsWith f t
218−
219−mconcatE :: (Reflex t m, Monoid a) => [Event t a] -> m (Event t a)
220−mconcatE = concatEventsWith mappend
221−-}
222
151
223−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the
224−-- respective value of the tuple.
225−splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
226−splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
227−
228−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
229−mconcatDyn :: forall t m a. (Reflex t, MonadHold t m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
230−mconcatDyn es = do
231− ddm :: Dynamic t (DMap (Const2 Int a) Identity) <- distributeDMapOverDyn . DMap.fromList . map (\(k, v) -> Const2 k :=> v) $ zip [0 :: Int ..] es
232− mapDyn (mconcat . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) ddm
233−
234−-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of
152+-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of 'Dynamic'
235−-- Dynamic values.
153+-- values. The resulting 'Dynamic' will change whenever one of the input
236−distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
237−distributeDMapOverDyn dm = case DMap.toList dm of
238− [] -> return $ constDyn DMap.empty
239− [k :=> v] -> mapDyn (DMap.singleton k . Identity) v
240− _ -> do
241− let edmPre = merge $ rewrapDMap updated dm
242− edm :: Event t (DMap k Identity) = flip push edmPre $ \o -> return . Just =<< do
243− let f _ = \case
244− This origDyn -> sample $ current origDyn
245− That _ -> error "distributeDMapOverDyn: should be impossible to have an event occurring that is not present in the original DMap"
246− These _ (Identity newVal) -> return newVal
247− sequenceDmap $ combineDMapsWithKey f dm (wrapDMap Identity o)
248− dm0 :: Behavior t (DMap k Identity) = pull $ do
249− liftM DMap.fromList . forM (DMap.toList dm) $ \(k :=> dv) -> liftM ((k :=>) . Identity) . sample $ current dv
250− bbdm :: Behavior t (Behavior t (DMap k Identity)) <- hold dm0 $ fmap constant edm
251− let bdm = pull $ sample =<< sample bbdm
252− return $ Dynamic bdm edm
253−
254−-- | Merge two 'Dynamic's into a new one using the provided
255−-- function. The new 'Dynamic' changes its value each time one of the
256−-- original 'Dynamic's changes its value.
154+-- 'Dynamic's changes.
257−combineDyn :: forall t m a b c. (Reflex t, MonadHold t m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
258−combineDyn f da db = do
259− let eab = align (updated da) (updated db)
260− ec = flip push eab $ \o -> do
261− (a, b) <- case o of
262− This a -> do
263− b <- sample $ current db
264− return (a, b)
265− That b -> do
266− a <- sample $ current da
267− return (a, b)
268− These a b -> return (a, b)
269− return $ Just $ f a b
270− c0 :: Behavior t c = pull $ liftM2 f (sample $ current da) (sample $ current db)
271− bbc :: Behavior t (Behavior t c) <- hold c0 $ fmap constant ec
272− let bc :: Behavior t c = pull $ sample =<< sample bbc
273− return $ Dynamic bc ec
274−
275−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
276--
155--
277−-- > do
156+-- This function's implementation is more efficient than simply using
278−-- > person <- Person `mapDyn` dynFirstName
157+-- 'zipDynWith' or applicative syntax to achieve the same result, especially for
279−-- > `apDyn` dynListName
158+-- maps with many elements.
280−-- > `apDyn` dynAge
281−-- > `apDyn` dynAddress
282−apDyn :: forall t m a b. (Reflex t, MonadHold t m)
283− => m (Dynamic t (a -> b))
284− -> Dynamic t a
285− -> m (Dynamic t b)
286−apDyn m a = do
287− r <- m
288− combineDyn (\f c -> f c) r a
289−
290−{-
291−tagInnerDyn :: Reflex t => Event t (Dynamic t a) -> Event t a
292−tagInnerDyn e =
293− let eSlow = push (liftM Just . sample . current) e
294− eFast = coincidence $ fmap updated e
295− in leftmost [eFast, eSlow]
296−-}
297−
298−-- | Join a nested 'Dynamic' into a new 'Dynamic' that has the value
299−-- of the inner 'Dynamic'.
300−joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
159+distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
301−joinDyn dd =
160+distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
302− let b' = pull $ sample . current =<< sample (current dd)
303− eOuter :: Event t a = pushAlways (sample . current) $ updated dd
304− eInner :: Event t a = switch $ fmap updated (current dd)
305− eBoth :: Event t a = coincidence $ fmap updated (updated dd)
306− e' = leftmost [eBoth, eOuter, eInner]
307− in Dynamic b' e'
308
161
309--TODO: Generalize this to functors other than Maps
162--TODO: Generalize this to functors other than Maps
310-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
163-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
311-- with the current values of the 'Dynamic's in a map.
164-- with the current values of the 'Dynamic's in a map.
312joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
165joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
313−joinDynThroughMap dd =
166+joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
314− let b' = pull $ mapM (sample . current) =<< sample (current dd)
315− eOuter :: Event t (Map k a) = pushAlways (mapM (sample . current)) $ updated dd
316− eInner :: Event t (Map k a) = attachWith (flip Map.union) b' $ switch $ fmap (mergeMap . fmap updated) (current dd) --Note: the flip is important because Map.union is left-biased
317− readNonFiring :: MonadSample t m => These (Dynamic t a) a -> m a
318− readNonFiring = \case
319− This d -> sample $ current d
320− That a -> return a
321− These _ a -> return a
322− eBoth :: Event t (Map k a) = coincidence $ fmap (\m -> pushAlways (mapM readNonFiring . align m) $ mergeMap $ fmap updated m) (updated dd)
323− e' = leftmost [eBoth, eOuter, eInner]
324− in Dynamic b' e'
325
167
326-- | Print the value of the 'Dynamic' on each change and prefix it
168-- | Print the value of the 'Dynamic' on each change and prefix it
327-- with the provided string. This should /only/ be used for debugging.
169-- with the provided string. This should /only/ be used for debugging.
@@ -340,42 +182,46 @@ traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
340traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
182traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
341traceDynWith f d =
183traceDynWith f d =
342 let e' = traceEventWith f $ updated d
184 let e' = traceEventWith f $ updated d
185+ getV0 = do
343− in Dynamic (current d) e'
186+ x <- sample $ current d
187+ trace (f x) $ return x
188+ in unsafeBuildDynamic getV0 e'
344
189
345-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
190-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
346-- each time the 'Event' occurs.
191-- each time the 'Event' occurs.
347--
192--
348−-- Note: `tagDyn d e` differs from `tag (current d) e` in the case that `e` is firing
193+-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
349−-- at the same time that `d` is changing. With `tagDyn d e`, the *new* value of `d`
194+-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
350-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
195-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
351-- will be used, since the 'Behavior' won't be updated until the end of the frame.
196-- will be used, since the 'Behavior' won't be updated until the end of the frame.
352-- Additionally, this means that the output 'Event' may not be used to directly change
197-- Additionally, this means that the output 'Event' may not be used to directly change
353-- the input 'Dynamic', because that would mean its value depends on itself. When creating
198-- the input 'Dynamic', because that would mean its value depends on itself. When creating
354-- cyclic data flows, generally `tag (current d) e` is preferred.
199-- cyclic data flows, generally `tag (current d) e` is preferred.
355−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
200+tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
356−tagDyn = attachDynWith const
201+tagPromptlyDyn = attachPromptlyDynWith const
357
202
358-- | Attach the current value of the 'Dynamic' to the value of the
203-- | Attach the current value of the 'Dynamic' to the value of the
359-- 'Event' each time it occurs.
204-- 'Event' each time it occurs.
360--
205--
361−-- Note: `attachDyn d` is not the same as `attach (current d)`. See 'tagDyn' for details.
206+-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
362−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
207+attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
363−attachDyn = attachDynWith (,)
208+attachPromptlyDyn = attachPromptlyDynWith (,)
364
209
365-- | Combine the current value of the 'Dynamic' with the value of the
210-- | Combine the current value of the 'Dynamic' with the value of the
366-- 'Event' each time it occurs.
211-- 'Event' each time it occurs.
367--
212--
368−-- Note: `attachDynWith f d` is not the same as `attachWith f (current d)`. See 'tagDyn' for details.
213+-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
369−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
214+attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
370−attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
215+attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
371
216
372−-- | Create a new 'Event' by combining the value at each occurence
217+-- | Create a new 'Event' by combining the value at each occurence with the
373−-- with the current value of the 'Dynamic' value and possibly
218+-- current value of the 'Dynamic' value and possibly filtering if the combining
374−-- filtering if the combining function returns 'Nothing'.
219+-- function returns 'Nothing'.
375--
220--
376−-- Note: `attachDynWithMaybe f d` is not the same as `attachWithMaybe f (current d)`. See 'tagDyn' for details.
221+-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
222+-- (current d)`. See 'tagPromptlyDyn' for details.
377−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
223+attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
378−attachDynWithMaybe f d e =
224+attachPromptlyDynWithMaybe f d e =
379 let e' = attach (current d) e
225 let e' = attach (current d) e
380 in fforMaybe (align e' $ updated d) $ \case
226 in fforMaybe (align e' $ updated d) $ \case
381 This (a, b) -> f a b -- Only the tagging event is firing, so use that
227 This (a, b) -> f a b -- Only the tagging event is firing, so use that
@@ -386,36 +232,38 @@ attachDynWithMaybe f d e =
386-- Demux
232-- Demux
387--------------------------------------------------------------------------------
233--------------------------------------------------------------------------------
388
234
389−-- | Represents a time changing value together with an 'EventSelector'
235+-- | Represents a time changing value together with an 'EventSelector' that can
390−-- that can efficiently detect when the underlying Dynamic has a particular value.
236+-- efficiently detect when the underlying 'Dynamic' has a particular value.
391−-- This is useful for representing data like the current selection of a long list.
237+-- This is useful for representing data like the current selection of a long
238+-- list.
392--
239--
393-- Semantically,
240-- Semantically,
241+--
394−-- > getDemuxed (demux d) k === mapDyn (== k) d
242+-- > demuxed (demux d) k === fmap (== k) d
243+--
395−-- However, the when getDemuxed is used multiple times, the complexity is only /O(log(n))/,
244+-- However, the when getDemuxed is used multiple times, the complexity is only
396−-- rather than /O(n)/ for mapDyn.
245+-- /O(log(n))/, rather than /O(n)/ for fmap.
397data Demux t k = Demux { demuxValue :: Behavior t k
246data Demux t k = Demux { demuxValue :: Behavior t k
398 , demuxSelector :: EventSelector t (Const2 k Bool)
247 , demuxSelector :: EventSelector t (Const2 k Bool)
399 }
248 }
400
249
250+-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
401−-- | Demultiplex an input value to a 'Demux' with many outputs. At any given time, whichever output is indicated by the given 'Dynamic' will be 'True'.
251+-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
402demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
252demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
403demux k = Demux (current k)
253demux k = Demux (current k)
404− (fan $ attachWith (\k0 k1 -> if k0 == k1
254+ (fan $ attachWith (\k0 k1 -> if k0 == k1
405 then DMap.empty
255 then DMap.empty
406 else DMap.fromList [Const2 k0 :=> Identity False,
256 else DMap.fromList [Const2 k0 :=> Identity False,
407 Const2 k1 :=> Identity True])
257 Const2 k1 :=> Identity True])
408 (current k) (updated k))
258 (current k) (updated k))
409
259
410−--TODO: The pattern of using hold (sample b0) can be reused in various places as a safe way of building certain kinds of Dynamics; see if we can factor this out
411−-- | Select a particular output of the 'Demux'; this is equivalent to (but much faster than)
260+-- | Select a particular output of the 'Demux'; this is equivalent to (but much
412−-- mapping over the original 'Dynamic' and checking whether it is equal to the given key.
261+-- faster than) mapping over the original 'Dynamic' and checking whether it is
262+-- equal to the given key.
413−getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
263+demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
414−getDemuxed d k = do
264+demuxed d k =
415 let e = select (demuxSelector d) (Const2 k)
265 let e = select (demuxSelector d) (Const2 k)
416− bb <- hold (liftM (==k) $ sample $ demuxValue d) $ fmap return e
266+ in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
417− let b = pull $ join $ sample bb
418− return $ Dynamic b e
419
267
420--------------------------------------------------------------------------------
268--------------------------------------------------------------------------------
421-- collectDyn
269-- collectDyn
@@ -423,6 +271,41 @@ getDemuxed d k = do
423
271
424--TODO: This whole section is badly in need of cleanup
272--TODO: This whole section is badly in need of cleanup
425
273
274+-- | A heterogeneous list whose type and length are fixed statically. This is
275+-- reproduced from the 'HList' package due to integration issues, and because
276+-- very little other functionality from that library is needed.
277+data HList (l::[*]) where
278+ HNil :: HList '[]
279+ HCons :: e -> HList l -> HList (e ': l)
280+
281+infixr 2 `HCons`
282+
283+type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
284+type instance HRevApp '[] l = l
285+type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
286+
287+hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
288+hRevApp HNil l = l
289+hRevApp (HCons x l) l' = hRevApp l (HCons x l')
290+
291+hReverse :: HList l -> HList (HRevApp l '[])
292+hReverse l = hRevApp l HNil
293+
294+hBuild :: (HBuild' '[] r) => r
295+hBuild = hBuild' HNil
296+
297+class HBuild' l r where
298+ hBuild' :: HList l -> r
299+
300+instance (l' ~ HRevApp l '[])
301+ => HBuild' l (HList l') where
302+ hBuild' = hReverse
303+
304+instance HBuild' (a ': l) r
305+ => HBuild' l (a->r) where
306+ hBuild' l x = hBuild' (HCons x l)
307+
308+-- | Like 'HList', but with a functor wrapping each element.
426data FHList f l where
309data FHList f l where
427 FHNil :: FHList f '[]
310 FHNil :: FHList f '[]
428 FHCons :: f e -> FHList f l -> FHList f (e ': l)
311 FHCons :: f e -> FHList f l -> FHList f (e ': l)
@@ -448,7 +331,7 @@ fhlistToDMap = DMap.fromList . go
448 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
331 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
449 go = \case
332 go = \case
450 FHNil -> []
333 FHNil -> []
451− FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> (HTailPtr p) :=> v) (go t)
334+ FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
452
335
453class RebuildSortedHList l where
336class RebuildSortedHList l where
454 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
337 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
@@ -473,16 +356,11 @@ instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
473dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
356dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
474dmapToHList = rebuildSortedHList . DMap.toList
357dmapToHList = rebuildSortedHList . DMap.toList
475
358
359+-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
360+-- single 'Dynamic' whose value represents the current values of all of the
361+-- input 'Dynamic's.
476−distributeFHListOverDyn :: forall t m l. (Reflex t, MonadHold t m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
362+distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
477−distributeFHListOverDyn l = mapDyn dmapToHList =<< distributeDMapOverDyn (fhlistToDMap l)
363+distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
478−{-
479−distributeFHListOverDyn l = do
480− let ec = undefined
481− c0 = pull $ sequenceFHList $ natMap (sample . current) l
482− bbc <- hold c0 $ fmap constant ec
483− let bc = pull $ sample =<< sample bbc
484− return $ Dynamic bc ec
485−-}
486
364
487class AllAreFunctors (f :: a -> *) (l :: [a]) where
365class AllAreFunctors (f :: a -> *) (l :: [a]) where
488 type FunctorList f l :: [*]
366 type FunctorList f l :: [*]
@@ -493,24 +371,30 @@ instance AllAreFunctors f '[] where
493 type FunctorList f '[] = '[]
371 type FunctorList f '[] = '[]
494 toFHList l = case l of
372 toFHList l = case l of
495 HNil -> FHNil
373 HNil -> FHNil
374+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
496 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
375 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
376+#endif
497 fromFHList FHNil = HNil
377 fromFHList FHNil = HNil
498
378
499instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
379instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
500 type FunctorList f (h ': t) = f h ': FunctorList f t
380 type FunctorList f (h ': t) = f h ': FunctorList f t
501 toFHList l = case l of
381 toFHList l = case l of
502 a `HCons` b -> a `FHCons` toFHList b
382 a `HCons` b -> a `FHCons` toFHList b
383+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
503 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
384 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
385+#endif
504 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
386 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
505
387
388+-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
389+-- single 'Dynamic' whose value represents all the current values of the input's
390+-- consitutent 'Dynamic's.
506−collectDyn :: ( RebuildSortedHList (HListElems b)
391+collectDynPure :: ( RebuildSortedHList (HListElems b)
507− , IsHList a, IsHList b
392+ , IsHList a, IsHList b
508− , AllAreFunctors (Dynamic t) (HListElems b)
393+ , AllAreFunctors (Dynamic t) (HListElems b)
509− , Reflex t, MonadHold t m
394+ , Reflex t
510− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
395+ , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
511− ) => a -> m (Dynamic t b)
396+ ) => a -> Dynamic t b
512−collectDyn ds =
513− mapDyn fromHList =<< distributeFHListOverDyn (toFHList $ toHList ds)
397+collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
514
398
515-- Poor man's Generic
399-- Poor man's Generic
516class IsHList a where
400class IsHList a where
@@ -523,18 +407,137 @@ instance IsHList (a, b) where
523 toHList (a, b) = hBuild a b
407 toHList (a, b) = hBuild a b
524 fromHList l = case l of
408 fromHList l = case l of
525 a `HCons` b `HCons` HNil -> (a, b)
409 a `HCons` b `HCons` HNil -> (a, b)
410+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
526 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
411 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
412+#endif
527
413
528instance IsHList (a, b, c, d) where
414instance IsHList (a, b, c, d) where
529 type HListElems (a, b, c, d) = [a, b, c, d]
415 type HListElems (a, b, c, d) = [a, b, c, d]
530 toHList (a, b, c, d) = hBuild a b c d
416 toHList (a, b, c, d) = hBuild a b c d
531 fromHList l = case l of
417 fromHList l = case l of
532 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
418 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
419+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
533 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
420 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
421+#endif
534
422
535instance IsHList (a, b, c, d, e, f) where
423instance IsHList (a, b, c, d, e, f) where
536 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
424 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
537 toHList (a, b, c, d, e, f) = hBuild a b c d e f
425 toHList (a, b, c, d, e, f) = hBuild a b c d e f
538 fromHList l = case l of
426 fromHList l = case l of
539 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
427 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
428+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
540 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
429 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
430+#endif
431+
432+--------------------------------------------------------------------------------
433+-- Deprecated functions
434+--------------------------------------------------------------------------------
435+
436+-- | Map a function over a 'Dynamic'.
437+{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
438+mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
439+mapDyn f = return . fmap f
440+
441+-- | Flipped version of 'mapDyn'.
442+{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
443+forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
444+forDyn a = return . ffor a
445+
446+-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
447+-- of the tuple.
448+{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
449+splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
450+splitDyn = return . splitDynPure
451+
452+-- | Merge the 'Dynamic' values using their 'Monoid' instance.
453+{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
454+mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
455+mconcatDyn = return . mconcat
456+
457+-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
458+{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
459+distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
460+distributeDMapOverDyn = return . distributeDMapOverDynPure
461+
462+-- | Merge two 'Dynamic's into a new one using the provided function. The new
463+-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
464+-- its value.
465+{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
466+combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
467+combineDyn f a b = return $ zipDynWith f a b
468+
469+-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
470+--
471+-- > do
472+-- > person <- Person `mapDyn` dynFirstName
473+-- > `apDyn` dynListName
474+-- > `apDyn` dynAge
475+-- > `apDyn` dynAddress
476+{-# DEPRECATED apDyn "Use 'ffor m (<*> a)' instead of 'apDyn m a'; consider eliminating monadic style, since Dynamics are now Applicative and can be used with applicative style directly" #-}
477+{-# ANN apDyn "HLint: ignore Use fmap" #-}
478+apDyn :: forall t m a b. (Reflex t, Monad m)
479+ => m (Dynamic t (a -> b))
480+ -> Dynamic t a
481+ -> m (Dynamic t b)
482+apDyn m a = liftM (<*> a) m
483+
484+--TODO: The pattern of using hold (sample b0) can be reused in various places as a safe way of building certain kinds of Dynamics; see if we can factor this out
485+-- | This function no longer needs to be monadic, so it has been replaced by
486+-- 'demuxed', which is pure.
487+{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
488+getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
489+getDemuxed d = return . demuxed d
490+
491+-- | This function no longer needs to be monadic, so it has been replaced by
492+-- 'distributeFHListOverDynPure', which is pure.
493+{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
494+distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
495+distributeFHListOverDyn = return . distributeFHListOverDynPure
496+
497+-- | This function no longer needs to be monadic, so it has been replaced by
498+-- 'collectDynPure', which is pure.
499+{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
500+collectDyn :: ( RebuildSortedHList (HListElems b)
501+ , IsHList a, IsHList b
502+ , AllAreFunctors (Dynamic t) (HListElems b)
503+ , Reflex t, Monad m
504+ , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
505+ ) => a -> m (Dynamic t b)
506+collectDyn = return . collectDynPure
507+
508+-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
509+-- semantics.
510+{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
511+tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
512+tagDyn = tagPromptlyDyn
513+
514+-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
515+-- semantics.
516+{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
517+attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
518+attachDyn = attachPromptlyDyn
519+
520+-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
521+-- semantics.
522+{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
523+attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
524+attachDynWith = attachPromptlyDynWith
525+
526+-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
527+-- its semantics.
528+{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
529+attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
530+attachDynWithMaybe = attachPromptlyDynWithMaybe
531+
532+-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
533+-- current value will always be equal to the current value's current value, and
534+-- will change whenever either the inner or the outer (or both) values change.
535+{-# DEPRECATED joinDyn "Use 'join' instead" #-}
536+joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
537+joinDyn = join
538+
539+-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
540+-- been renamed to 'uniqDyn'
541+{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
542+nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
543+nubDyn = uniqDyn
src/Reflex/Dynamic/TH.hs+32−11
@@ -1,19 +1,27 @@
1−{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TypeOperators, GADTs, EmptyDataDecls, PatternGuards #-}
1+{-# LANGUAGE EmptyDataDecls #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE PatternGuards #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE TemplateHaskell #-}
6+{-# LANGUAGE TypeOperators #-}
7+-- | Template Haskell helper functions for building complex 'Dynamic' values.
2module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
8module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
3
9
4import Reflex.Dynamic
10import Reflex.Dynamic
5
11
6−import Language.Haskell.TH
7−import qualified Language.Haskell.TH.Syntax as TH
8−import Language.Haskell.TH.Quote
9−import Data.Data
10import Control.Monad.State
12import Control.Monad.State
13+import Data.Data
14+import Data.Generics
15+import Data.Monoid
11import qualified Language.Haskell.Exts as Hs
16import qualified Language.Haskell.Exts as Hs
12import qualified Language.Haskell.Meta.Syntax.Translate as Hs
17import qualified Language.Haskell.Meta.Syntax.Translate as Hs
13−import Data.Monoid
18+import Language.Haskell.TH
14−import Data.Generics
19+import Language.Haskell.TH.Quote
20+import qualified Language.Haskell.TH.Syntax as TH
15
21
16−-- | Quote a Dynamic expression. Within the quoted expression, you can use '$(unqDyn [| x |])' to refer to any expression 'x' of type 'Dynamic t a'; the unquoted result will be of type 'a'
22+-- | Quote a 'Dynamic' expression. Within the quoted expression, you can use
23+-- @$(unqDyn [| x |])@ to refer to any expression @x@ of type @Dynamic t a@; the
24+-- unquoted result will be of type @a@
17qDyn :: Q Exp -> Q Exp
25qDyn :: Q Exp -> Q Exp
18qDyn qe = do
26qDyn qe = do
19 e <- qe
27 e <- qe
@@ -30,19 +38,32 @@ qDyn qe = do
30 let exprs = reverse exprsReversed
38 let exprs = reverse exprsReversed
31 arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
39 arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
32 param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
40 param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
33− [| mapDyn $(return $ LamE [param] e') =<< distributeFHListOverDyn $(return arg) |]
41+ [| return $ $(return $ LamE [param] e') $ distributeFHListOverDynPure $(return arg) |]
34
42
43+-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
44+-- 'qDyn' quotation.
35unqDyn :: Q Exp -> Q Exp
45unqDyn :: Q Exp -> Q Exp
36unqDyn e = [| unqMarker $e |]
46unqDyn e = [| unqMarker $e |]
37
47
38−-- | This type represents an occurrence of unqDyn before it has been processed by qDyn. If you see it in a type error, it probably means that unqDyn has been used outside of a qDyn context.
48+-- | This type represents an occurrence of unqDyn before it has been processed
49+-- by qDyn. If you see it in a type error, it probably means that unqDyn has
50+-- been used outside of a qDyn context.
39data UnqDyn
51data UnqDyn
40
52
41−-- unqMarker must not be exported; it is used only as a way of smuggling data from unqDyn to qDyn
53+-- unqMarker must not be exported; it is used only as a way of smuggling data
54+-- from unqDyn to qDyn
55+
42--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
56--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
43unqMarker :: a -> UnqDyn
57unqMarker :: a -> UnqDyn
44unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
58unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
45
59
60+-- | Create a 'Dynamic' value using other 'Dynamic's as inputs. The result is
61+-- sometimes more concise and readable than the equivalent 'Applicative'-based
62+-- expression. For example:
63+--
64+-- > [mkDyn| $x + $v * $t + 1/2 * $a * $t ^ 2 |]
65+--
66+-- would have a very cumbersome 'Applicative' encoding.
46mkDyn :: QuasiQuoter
67mkDyn :: QuasiQuoter
47mkDyn = QuasiQuoter
68mkDyn = QuasiQuoter
48 { quoteExp = mkDynExp
69 { quoteExp = mkDynExp
src/Reflex/Dynamic/Uniq.hsnew file+101
@@ -0,0 +1,101 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE MagicHash #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+-- | This module provides a variation of 'Dynamic' values that uses cheap
5+-- pointer equality checks to reduce the amount of signal propagation needed.
6+module Reflex.Dynamic.Uniq
7+ ( UniqDynamic
8+ , uniqDynamic
9+ , fromUniqDynamic
10+ , alreadyUniqDynamic
11+ ) where
12+
13+import Control.Applicative (Applicative (..))
14+import GHC.Exts
15+import Reflex.Class
16+import Reflex.Dynamic
17+
18+-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
19+-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
20+-- the value inside a 'UniqDynamic' is always evaluated to
21+-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
22+--
23+-- Internally, 'UniqDynamic' uses pointer equality as a heuristic to avoid
24+-- unnecessary update propagation; this is much more efficient than performing
25+-- full comparisons. However, when the 'UniqDynamic' is converted back into a
26+-- regular 'Dynamic', a full comparison is performed.
27+newtype UniqDynamic t a = UniqDynamic { unUniqDynamic :: Dynamic t a }
28+
29+-- | Construct a 'UniqDynamic' by eliminating redundant updates from a 'Dynamic'.
30+uniqDynamic :: Reflex t => Dynamic t a -> UniqDynamic t a
31+uniqDynamic d = UniqDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
32+ old <- sample $ current d --TODO: Is it better to sample ourselves here?
33+ return $ unsafeJustChanged old new
34+
35+-- | Retrieve a normal 'Dynamic' from a 'UniqDynamic'. This will perform a
36+-- final check using the output type's 'Eq' instance to ensure deterministic
37+-- behavior.
38+--
39+-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
40+-- specifically, if there are cases where @x /= x@, 'fromUniqDynamic' may
41+-- eliminate more 'updated' occurrences than it should. For example, NaN values
42+-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
43+-- instance, but can be equal by pointer equality. This may cause 'UniqDynamic'
44+-- to lose changes from NaN to NaN.
45+fromUniqDynamic :: (Reflex t, Eq a) => UniqDynamic t a -> Dynamic t a
46+fromUniqDynamic (UniqDynamic d) = uniqDynBy superEq d
47+ where
48+ -- Only consider values different if they fail both pointer equality /and/
49+ -- 'Eq' equality. This is to make things a bit more deterministic in the
50+ -- case of unlawful 'Eq' instances. However, it is still possible to
51+ -- achieve nondeterminism by constructing elements that are identical in
52+ -- value, unequal according to 'Eq', and nondeterministically equal or
53+ -- nonequal by pointer quality. I suspect that it is impossible to make the
54+ -- behavior deterministic in this case.
55+ superEq a b = a `unsafePtrEq` b || a == b
56+
57+-- | Create a UniqDynamic without uniqing it on creation. This will be slightly
58+-- faster than uniqDynamic when used with a Dynamic whose values are always (or
59+-- nearly always) different from its previous values; if used with a Dynamic
60+-- whose values do not change frequently, it may be much slower than uniqDynamic
61+alreadyUniqDynamic :: Dynamic t a -> UniqDynamic t a
62+alreadyUniqDynamic = UniqDynamic
63+
64+unsafePtrEq :: a -> a -> Bool
65+unsafePtrEq a b = case a `seq` b `seq` reallyUnsafePtrEquality# a b of
66+ 0# -> False
67+ _ -> True
68+
69+unsafeJustChanged :: a -> a -> Maybe a
70+unsafeJustChanged old new =
71+ if old `unsafePtrEq` new
72+ then Nothing
73+ else Just new
74+
75+instance Reflex t => Accumulator t (UniqDynamic t) where
76+ accumMaybeM f z e = do
77+ let f' old change = do
78+ mNew <- f old change
79+ return $ unsafeJustChanged old =<< mNew
80+ d <- accumMaybeM f' z e
81+ return $ UniqDynamic d
82+ mapAccumMaybeM f z e = do
83+ let f' old change = do
84+ (mNew, output) <- f old change
85+ return (unsafeJustChanged old =<< mNew, output)
86+ (d, out) <- mapAccumMaybeM f' z e
87+ return (UniqDynamic d, out)
88+
89+instance Reflex t => Functor (UniqDynamic t) where
90+ fmap f (UniqDynamic d) = uniqDynamic $ fmap f d
91+
92+instance Reflex t => Applicative (UniqDynamic t) where
93+ pure = UniqDynamic . constDyn
94+ UniqDynamic a <*> UniqDynamic b = uniqDynamic $ a <*> b
95+ _ *> b = b
96+ a <* _ = a
97+
98+instance Reflex t => Monad (UniqDynamic t) where
99+ UniqDynamic x >>= f = uniqDynamic $ x >>= unUniqDynamic . f
100+ _ >> b = b
101+ return = pure
src/Reflex/Host/Class.hs+110−55
@@ -1,5 +1,28 @@
1−{-# LANGUAGE ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, FunctionalDependencies, FlexibleContexts #-}
1+{-# LANGUAGE ExistentialQuantification #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE RankNTypes #-}
8+{-# LANGUAGE RoleAnnotations #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE UndecidableInstances #-}
12+-- | 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
14+-- that provides a core event loop.
2−module Reflex.Host.Class where
15+module Reflex.Host.Class
16+ ( ReflexHost (..)
17+ , MonadSubscribeEvent (..)
18+ , MonadReadEvent (..)
19+ , MonadReflexCreateTrigger (..)
20+ , MonadReflexHost (..)
21+ , fireEvents
22+ , newEventWithTriggerRef
23+ , fireEventRef
24+ , fireEventRefAndRead
25+ ) where
3
26
4import Reflex.Class
27import Reflex.Class
5
28
@@ -7,49 +30,63 @@ import Control.Applicative
7import Control.Monad
30import Control.Monad
8import Control.Monad.Fix
31import Control.Monad.Fix
9import Control.Monad.Identity
32import Control.Monad.Identity
33+import Control.Monad.Ref
10import Control.Monad.Trans
34import Control.Monad.Trans
11−import Control.Monad.Trans.Reader (ReaderT())
35+import Control.Monad.Trans.Cont (ContT ())
12−import Control.Monad.Trans.Writer (WriterT())
13−import Control.Monad.Trans.Cont (ContT())
14−import Control.Monad.Trans.Except (ExceptT())
36+import Control.Monad.Trans.Except (ExceptT ())
37+import Control.Monad.Trans.Reader (ReaderT ())
15−import Control.Monad.Trans.RWS (RWST())
38+import Control.Monad.Trans.RWS (RWST ())
16−import Control.Monad.Trans.State (StateT())
39+import Control.Monad.Trans.State (StateT ())
17import qualified Control.Monad.Trans.State.Strict as Strict
40import qualified Control.Monad.Trans.State.Strict as Strict
41+import Control.Monad.Trans.Writer (WriterT ())
18import Data.Dependent.Sum (DSum (..))
42import Data.Dependent.Sum (DSum (..))
19−import Data.Monoid
20import Data.GADT.Compare
43import Data.GADT.Compare
21−import Control.Monad.Ref
44+import Data.Monoid
22
45
23-- Note: this import must come last to silence warnings from AMP
46-- Note: this import must come last to silence warnings from AMP
24−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
47+import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
25−
48+
26−-- | Framework implementation support class for the reflex implementation represented by @t@.
49+-- | Framework implementation support class for the reflex implementation
27−class (Reflex t, MonadReflexCreateTrigger t (HostFrame t), MonadSample t (HostFrame t), MonadHold t (HostFrame t), MonadFix (HostFrame t), MonadSubscribeEvent t (HostFrame t)) => ReflexHost t where
50+-- represented by @t@.
51+class ( Reflex t
52+ , MonadReflexCreateTrigger t (HostFrame t)
53+ , MonadSample t (HostFrame t)
54+ , MonadHold t (HostFrame t)
55+ , MonadFix (HostFrame t)
56+ , MonadSubscribeEvent t (HostFrame t)
57+ ) => ReflexHost t where
28 type EventTrigger t :: * -> *
58 type EventTrigger t :: * -> *
29 type EventHandle t :: * -> *
59 type EventHandle t :: * -> *
30 type HostFrame t :: * -> *
60 type HostFrame t :: * -> *
31
61
62+-- | Monad in which Events can be 'subscribed'. This forces all underlying
63+-- event sources to be initialized, so that the event will fire whenever it
32−-- | Monad in which Events can be 'subscribed'. This forces all underlying event sources to be initialized, so that the event will fire whenever it ought to. Events must be subscribed before they are read using readEvent
64+-- ought to. Events must be subscribed before they are read using readEvent
33class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
65class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
34 -- | Subscribe to an event and set it up if needed.
66 -- | Subscribe to an event and set it up if needed.
35 --
67 --
36− -- This function will create a new 'EventHandle' from an 'Event'. This handle may then be used via
68+ -- This function will create a new 'EventHandle' from an 'Event'. This handle
37− -- 'readEvent' in the read callback of 'fireEventsAndRead'.
69+ -- may then be used via 'readEvent' in the read callback of
70+ -- 'fireEventsAndRead'.
38 --
71 --
39− -- If the event wasn't subscribed to before (either manually or through a dependent event or behavior)
72+ -- If the event wasn't subscribed to before (either manually or through a
40− -- then this function will cause the event and all dependencies of this event to be set up.
73+ -- dependent event or behavior) then this function will cause the event and
74+ -- all dependencies of this event to be set up. For example, if the event was
41− -- For example, if the event was created by 'newEventWithTrigger', then it's callback will be executed.
75+ -- created by 'newEventWithTrigger', then it's callback will be executed.
42 --
76 --
43 -- It's safe to call this function multiple times.
77 -- It's safe to call this function multiple times.
44 subscribeEvent :: Event t a -> m (EventHandle t a)
78 subscribeEvent :: Event t a -> m (EventHandle t a)
45
79
46-- | Monad that allows to read events' values.
80-- | Monad that allows to read events' values.
47class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
81class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
48− -- | Read the value of an 'Event' from an 'EventHandle' (created by calling 'subscribeEvent').
82+ -- | Read the value of an 'Event' from an 'EventHandle' (created by calling
83+ -- 'subscribeEvent').
49 --
84 --
50− -- After event propagation is done, all events can be in two states: either they are firing with some value or they are not firing.
85+ -- After event propagation is done, all events can be in two states: either
86+ -- they are firing with some value or they are not firing. In the former
51− -- In the former case, this function returns @Just act@, where @act@ in an action to read the current value of
87+ -- case, this function returns @Just act@, where @act@ in an action to read
52− -- the event. In the latter case, the function returns @Nothing@.
88+ -- the current value of the event. In the latter case, the function returns
89+ -- @Nothing@.
53 --
90 --
54 -- This function is normally used in the calllback for 'fireEventsAndRead'.
91 -- This function is normally used in the calllback for 'fireEventsAndRead'.
55 readEvent :: EventHandle t a -> m (Maybe (m a))
92 readEvent :: EventHandle t a -> m (Maybe (m a))
@@ -58,42 +95,54 @@ class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t wher
58class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
95class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
59 -- | Creates a root 'Event' (one that is not based on any other event).
96 -- | Creates a root 'Event' (one that is not based on any other event).
60 --
97 --
61− -- When a subscriber first subscribes to an event (building another event
98+ -- When a subscriber first subscribes to an event (building another event that
62− -- that depends on the subscription) the given callback function is run and
99+ -- depends on the subscription) the given callback function is run and passed
63− -- passed a trigger. The callback function can then set up the event source in IO.
100+ -- a trigger. The callback function can then set up the event source in IO.
64− -- After this is done, the callback function must return an accompanying teardown action.
101+ -- After this is done, the callback function must return an accompanying
102+ -- teardown action.
65 --
103 --
66− -- Any time between setup and teardown the trigger can be used to fire
104+ -- Any time between setup and teardown the trigger can be used to fire the
67− -- the event, by passing it to 'fireEventsAndRead'.
105+ -- event, by passing it to 'fireEventsAndRead'.
68 --
106 --
69− -- Note: An event may be set up multiple times. So after the teardown action is executed, the event may still be set up again in the future.
107+ -- Note: An event may be set up multiple times. So after the teardown action
108+ -- is executed, the event may still be set up again in the future.
70 newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
109 newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
71 newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
110 newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
72
111
73−class (ReflexHost t, MonadReflexCreateTrigger t m, MonadSubscribeEvent t m, MonadReadEvent t (ReadPhase m), MonadSample t (ReadPhase m), MonadHold t (ReadPhase m)) => MonadReflexHost t m | m -> t where
112+-- | 'MonadReflexHost' designates monads that can run reflex frames.
113+class ( ReflexHost t
114+ , MonadReflexCreateTrigger t m
115+ , MonadSubscribeEvent t m
116+ , MonadReadEvent t (ReadPhase m)
117+ , MonadSample t (ReadPhase m)
118+ , MonadHold t (ReadPhase m)
119+ ) => MonadReflexHost t m | m -> t where
74 type ReadPhase m :: * -> *
120 type ReadPhase m :: * -> *
75 -- | Propagate some events firings and read the values of events afterwards.
121 -- | Propagate some events firings and read the values of events afterwards.
76 --
122 --
77− -- This function will create a new frame to fire the given events. It will then update all
123+ -- This function will create a new frame to fire the given events. It will
78− -- dependent events and behaviors. After that is done, the given callback is executed which
124+ -- then update all dependent events and behaviors. After that is done, the
125+ -- given callback is executed which allows to read the final values of events
79− -- allows to read the final values of events and check whether they have fired in this frame or
126+ -- and check whether they have fired in this frame or not.
80− -- not.
81 --
127 --
82 -- All events that are given are fired at the same time.
128 -- All events that are given are fired at the same time.
83 --
129 --
84− -- This function is typically used in the main loop of a reflex framework implementation.
130+ -- This function is typically used in the main loop of a reflex framework
85− -- The main loop waits for external events to happen (such as keyboard input or a mouse click)
131+ -- implementation. The main loop waits for external events to happen (such as
132+ -- keyboard input or a mouse click) and then fires the corresponding events
86− -- and then fires the corresponding events using this function. The read callback can be used
133+ -- using this function. The read callback can be used to read output events
87− -- to read output events and perform a corresponding response action to the external event.
134+ -- and perform a corresponding response action to the external event.
88− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> (ReadPhase m a) -> m a
135+ fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
89
136
90 -- | Run a frame without any events firing.
137 -- | Run a frame without any events firing.
91 --
138 --
92− -- This function should be used when you want to use 'sample' and 'hold' when no events are currently firing.
139+ -- This function should be used when you want to use 'sample' and 'hold' when
140+ -- no events are currently firing. Using this function in that case can
93− -- Using this function in that case can improve performance, since the implementation can assume that no events
141+ -- improve performance, since the implementation can assume that no events are
94− -- are firing when 'sample' or 'hold' are called.
142+ -- firing when 'sample' or 'hold' are called.
95 --
143 --
96− -- This function is commonly used to set up the basic event network when the application starts up.
144+ -- This function is commonly used to set up the basic event network when the
145+ -- application starts up.
97 runHostFrame :: HostFrame t a -> m a
146 runHostFrame :: HostFrame t a -> m a
98
147
99-- | Like 'fireEventsAndRead', but without reading any events.
148-- | Like 'fireEventsAndRead', but without reading any events.
@@ -103,10 +152,13 @@ fireEvents dm = fireEventsAndRead dm $ return ()
103
152
104-- | Create a new event and store its trigger in an 'IORef' while it's active.
153-- | Create a new event and store its trigger in an 'IORef' while it's active.
105--
154--
106−-- An event is only active between the set up (when it's first subscribed to) and the teardown phases (when noboby is subscribing the event anymore).
155+-- An event is only active between the set up (when it's first subscribed to)
156+-- and the teardown phases (when noboby is subscribing the event anymore). This
107−-- This function returns an Event and an 'IORef'. As long as the event is active, the 'IORef' will contain 'Just' the event trigger to
157+-- function returns an Event and an 'IORef'. As long as the event is active, the
158+-- 'IORef' will contain 'Just' the event trigger to trigger this event. When the
108−-- trigger this event. When the event is not active, the 'IORef' will contain 'Nothing'. This allows event sources to be more efficient,
159+-- event is not active, the 'IORef' will contain 'Nothing'. This allows event
109−-- since they don't need to produce events when nobody is listening.
160+-- sources to be more efficient, since they don't need to produce events when
161+-- nobody is listening.
110newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
162newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
111newEventWithTriggerRef = do
163newEventWithTriggerRef = do
112 rt <- newRef Nothing
164 rt <- newRef Nothing
@@ -116,6 +168,7 @@ newEventWithTriggerRef = do
116 return (e, rt)
168 return (e, rt)
117{-# INLINE newEventWithTriggerRef #-}
169{-# INLINE newEventWithTriggerRef #-}
118
170
171+-- | Fire the given trigger if it is not 'Nothing'.
119fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
172fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
120fireEventRef mtRef input = do
173fireEventRef mtRef input = do
121 mt <- readRef mtRef
174 mt <- readRef mtRef
@@ -123,6 +176,8 @@ fireEventRef mtRef input = do
123 Nothing -> return ()
176 Nothing -> return ()
124 Just trigger -> fireEvents [trigger :=> Identity input]
177 Just trigger -> fireEvents [trigger :=> Identity input]
125
178
179+-- | Fire the given trigger if it is not 'Nothing', and read from the given
180+-- 'EventHandle'.
126fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
181fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
127fireEventRefAndRead mtRef input e = do
182fireEventRefAndRead mtRef input e = do
128 mt <- readRef mtRef
183 mt <- readRef mtRef
@@ -132,7 +187,7 @@ fireEventRefAndRead mtRef input e = do
132 mGetValue <- readEvent e
187 mGetValue <- readEvent e
133 case mGetValue of
188 case mGetValue of
134 Nothing -> return Nothing
189 Nothing -> return Nothing
135− Just getValue -> liftM Just getValue
190+ Just getValue -> fmap Just getValue
136
191
137
192
138--------------------------------------------------------------------------------
193--------------------------------------------------------------------------------
@@ -174,8 +229,8 @@ instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
174 type ReadPhase (StateT s m) = ReadPhase m
229 type ReadPhase (StateT s m) = ReadPhase m
175 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
230 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
176 runHostFrame = lift . runHostFrame
231 runHostFrame = lift . runHostFrame
177−
232+
178−
233+
179instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
234instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
180 newEventWithTrigger = lift . newEventWithTrigger
235 newEventWithTrigger = lift . newEventWithTrigger
181 newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
236 newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
@@ -186,9 +241,9 @@ instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) wh
186instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
241instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
187 type ReadPhase (Strict.StateT s m) = ReadPhase m
242 type ReadPhase (Strict.StateT s m) = ReadPhase m
188 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
243 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
189− runHostFrame = lift . runHostFrame
244+ runHostFrame = lift . runHostFrame
190−
245+
191−
246+
192
247
193instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
248instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
194 newEventWithTrigger = lift . newEventWithTrigger
249 newEventWithTrigger = lift . newEventWithTrigger
src/Reflex/Spider.hs+6−1
@@ -1,5 +1,10 @@
1+-- | This module exports all of the user-facing functionality of the 'Spider'
2+-- 'Reflex' engine
1module Reflex.Spider ( Spider
3module Reflex.Spider ( Spider
2− , SpiderHost (..) --Temporary, until Widget no longer uses unsafePerformIO
4+ , SpiderEnv
5+ , Global
6+ , SpiderHost
7+ , runSpiderHost
3 ) where
8 ) where
4
9
5import Reflex.Spider.Internal
10import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hs+1608−764
@@ -1,53 +1,104 @@
1−{-# LANGUAGE CPP, ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, LambdaCase, TypeOperators #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE ConstraintKinds #-}
4+{-# LANGUAGE EmptyDataDecls #-}
5+{-# LANGUAGE ExistentialQuantification #-}
6+{-# LANGUAGE FlexibleContexts #-}
7+{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE FunctionalDependencies #-}
9+{-# LANGUAGE GADTs #-}
10+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11+{-# LANGUAGE LambdaCase #-}
12+{-# LANGUAGE MagicHash #-}
13+{-# LANGUAGE MultiParamTypeClasses #-}
14+{-# LANGUAGE MultiWayIf #-}
15+{-# LANGUAGE RankNTypes #-}
16+{-# LANGUAGE RoleAnnotations #-}
17+{-# LANGUAGE ScopedTypeVariables #-}
18+{-# LANGUAGE TypeFamilies #-}
19+{-# LANGUAGE TypeOperators #-}
20+{-# LANGUAGE UndecidableInstances #-}
21+-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
22+-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2−module Reflex.Spider.Internal where
23+module Reflex.Spider.Internal
3−
24+ ( Spider
4−import qualified Reflex.Class as R
25+ , SpiderEnv (..)
5−import qualified Reflex.Host.Class as R
26+ , Global
6−
27+ , SpiderHost
7−import Data.IORef
28+ , runSpiderHost
8−import System.Mem.Weak
29+ , newSpiderEnv
9−import Data.Foldable
30+ , askSpiderEnv
10−import Data.Traversable
31+ ) where
32+
33+import Control.Applicative
34+import Control.Concurrent
35+import Control.Exception
11−import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
36+import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
12−import Control.Monad.Identity hiding (mapM, mapM_, forM_, forM, sequence)
37+import Control.Monad.Exception
38+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
13−import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
39+import Control.Monad.Primitive
14−import GHC.Exts
40+import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
15−import Control.Applicative -- Unconditionally import, because otherwise it breaks on GHC 7.10.1RC2
41+import Control.Monad.Ref
42+import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
43+import Data.Align
44+import Data.Coerce
16import Data.Dependent.Map (DMap, DSum (..))
45import Data.Dependent.Map (DMap, DSum (..))
17import qualified Data.Dependent.Map as DMap
46import qualified Data.Dependent.Map as DMap
47+import Data.Foldable hiding (concat, elem, sequence_)
48+import Data.Function
49+import Data.Functor.Compose
18import Data.GADT.Compare
50import Data.GADT.Compare
51+import Data.IntMap.Strict (IntMap)
52+import qualified Data.IntMap.Strict as IntMap
53+import Data.IORef
54+import Data.List hiding (foldl')
55+import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
56+import qualified Data.List.NonEmpty as NonEmpty
19import Data.Maybe
57import Data.Maybe
20−import Data.IntMap (IntMap)
58+import Data.Monoid (mempty, (<>))
21−import qualified Data.IntMap as IntMap
22−import Control.Monad.Ref
23−import Control.Monad.Exception
24−import Data.Monoid ((<>))
25−import Data.Coerce
59+import Data.Some (Some)
26−import Control.Exception
60+import qualified Data.Some as Some
27−
61+import Data.These
62+import Data.Traversable
63+import Data.Tree (Forest, Tree (..), drawForest)
64+import Data.Word
65+import GHC.IORef (IORef (..))
66+import GHC.Stack
28import System.IO.Unsafe
67import System.IO.Unsafe
68+import System.Mem.Weak
29import Unsafe.Coerce
69import Unsafe.Coerce
30−import Control.Monad.Primitive
70+
71+import Data.WeakBag (WeakBag, WeakBagTicket)
72+import qualified Data.WeakBag as WeakBag
73+import qualified Reflex.Class as R
74+import qualified Reflex.Host.Class as R
31
75
32-- Note: must come last to silence warnings due to AMP on GHC < 7.10
76-- Note: must come last to silence warnings due to AMP on GHC < 7.10
33−import Prelude hiding (mapM, mapM_, any, sequence, concat)
77+import Prelude hiding (any, concat, mapM, mapM_, sequence)
78+
79+type SubscriberList a = WeakBag (Subscriber a)
80+type SubscriberListTicket a = WeakBagTicket (Subscriber a)
34
81
35debugPropagate :: Bool
82debugPropagate :: Bool
36
83
37debugInvalidateHeight :: Bool
84debugInvalidateHeight :: Bool
38
85
86+debugInvalidate :: Bool
87+
39#ifdef DEBUG
88#ifdef DEBUG
40
89
41#define DEBUG_NODEIDS
90#define DEBUG_NODEIDS
42
91
43debugPropagate = True
92debugPropagate = True
44
93
45−debugInvalidateHeight = True
94+debugInvalidateHeight = False
95+
96+debugInvalidate = True
46
97
47class HasNodeId a where
98class HasNodeId a where
48 getNodeId :: a -> Int
99 getNodeId :: a -> Int
49
100
50−instance HasNodeId (Hold a) where
101+instance HasNodeId (Hold p a) where
51 getNodeId = holdNodeId
102 getNodeId = holdNodeId
52
103
53instance HasNodeId (PushSubscribed a b) where
104instance HasNodeId (PushSubscribed a b) where
@@ -68,17 +119,52 @@ instance HasNodeId (CoincidenceSubscribed a) where
68instance HasNodeId (RootSubscribed a) where
119instance HasNodeId (RootSubscribed a) where
69 getNodeId = rootSubscribedNodeId
120 getNodeId = rootSubscribedNodeId
70
121
122+instance HasNodeId (Pull a) where
123+ getNodeId = pullNodeId
124+
125+{-# INLINE showNodeId #-}
71showNodeId :: HasNodeId a => a -> String
126showNodeId :: HasNodeId a => a -> String
72showNodeId = ("#"<>) . show . getNodeId
127showNodeId = ("#"<>) . show . getNodeId
73
128
129+showSubscriberType :: Subscriber a -> String
130+showSubscriberType = \case
131+ SubscriberPush _ _ -> "SubscriberPush"
132+ SubscriberMerge _ _ -> "SubscriberMerge"
133+ SubscriberFan _ -> "SubscriberFan"
134+ SubscriberMergeChange _ -> "SubscriberMergeChange"
135+ SubscriberHold _ -> "SubscriberHold"
136+ SubscriberHoldIdentity _ -> "SubscriberHoldIdentity"
137+ SubscriberSwitch _ -> "SubscriberSwitch"
138+ SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
139+ SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
140+ SubscriberHandle -> "SubscriberHandle"
141+
142+showEventType :: Event a -> String
143+showEventType = \case
144+ EventRoot _ _ -> "EventRoot"
145+ EventNever -> "EventNever"
146+ EventPush _ -> "EventPush"
147+ EventMerge _ -> "EventMerge"
148+ EventFan _ _ -> "EventFan"
149+ EventSwitch _ -> "EventSwitch"
150+ EventCoincidence _ -> "EventCoincidence"
151+ EventHold _ -> "EventHold"
152+ EventDyn _ -> "EventDyn"
153+ EventHoldIdentity _ -> "EventHoldIdentity"
154+ EventDynIdentity _ -> "EventDynIdentity"
155+
74#else
156#else
75
157
76debugPropagate = False
158debugPropagate = False
77
159
78debugInvalidateHeight = False
160debugInvalidateHeight = False
79
161
162+debugInvalidate = False
163+
164+-- This must be inline, or error messages will cause memory leaks due to retaining the node in question
165+{-# INLINE showNodeId #-}
80showNodeId :: a -> String
166showNodeId :: a -> String
81−showNodeId = const ""
167+showNodeId _ = ""
82
168
83#endif
169#endif
84
170
@@ -87,116 +173,189 @@ showNodeId = const ""
87nextNodeIdRef :: IORef Int
173nextNodeIdRef :: IORef Int
88nextNodeIdRef = unsafePerformIO $ newIORef 1
174nextNodeIdRef = unsafePerformIO $ newIORef 1
89
175
176+newNodeId :: IO Int
177+newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
178+
90{-# NOINLINE unsafeNodeId #-}
179{-# NOINLINE unsafeNodeId #-}
91unsafeNodeId :: a -> Int
180unsafeNodeId :: a -> Int
92unsafeNodeId a = unsafePerformIO $ do
181unsafeNodeId a = unsafePerformIO $ do
93 touch a
182 touch a
94− atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
183+ newNodeId
95#endif
184#endif
96
185
97--TODO: Figure out why certain things are not 'representational', then make them representational so we can use coerce
186--TODO: Figure out why certain things are not 'representational', then make them representational so we can use coerce
98--type role Hold representational
187--type role Hold representational
99−data Hold a
188+data Hold p a
100 = Hold { holdValue :: !(IORef a)
189 = Hold { holdValue :: !(IORef a)
101 , holdInvalidators :: !(IORef [Weak Invalidator])
190 , holdInvalidators :: !(IORef [Weak Invalidator])
102− -- We need to use 'Any' for the next two things, because otherwise Hold inherits a nominal role for its 'a' parameter, and we want to be able to use 'coerce'
103− , holdSubscriber :: !(IORef Any) -- Keeps its subscription alive; for some reason, a regular (or strict) reference to the Subscriber itself wasn't working, so had to use an IORef
104− , holdParent :: !(IORef Any) -- Keeps its parent alive (will be undefined until the hold is initialized --TODO: Probably shouldn't be an IORef
191+ , 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
105#ifdef DEBUG_NODEIDS
192#ifdef DEBUG_NODEIDS
106 , holdNodeId :: Int
193 , holdNodeId :: Int
107#endif
194#endif
108 }
195 }
109
196
110−data EventEnv
197+data EventEnv x
198+ = EventEnv { eventEnvSpiderEnv :: !(SpiderEnv x)
111− = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment])
199+ , eventEnvAssignments :: !(IORef [SomeAssignment]) -- Needed for Subscribe
112− , eventEnvHoldInits :: !(IORef [SomeHoldInit])
200+ , eventEnvHoldInits :: !(IORef [SomeHoldInit]) -- Needed for Subscribe
201+ , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate])
202+ , eventEnvMergeInits :: !(IORef [SomeMergeInit]) -- Needed for Subscribe
113− , eventEnvClears :: !(IORef [SomeMaybeIORef])
203+ , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
114− , eventEnvRootClears :: !(IORef [SomeDMapIORef])
204+ , eventEnvRootClears :: !(IORef [SomeRootClear])
115− , eventEnvCurrentHeight :: !(IORef Int)
205+ , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
116− , eventEnvCoincidenceInfos :: !(IORef [SomeCoincidenceInfo])
206+ , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence]) -- Needed for Subscribe
117 , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
207 , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
118 }
208 }
119
209
210+{-# INLINE runEventM #-}
120−runEventM :: EventM a -> EventEnv -> IO a
211+runEventM :: EventM x a -> EventEnv x -> IO a
121runEventM = runReaderT . unEventM
212runEventM = runReaderT . unEventM
122
213
123−askToAssignRef :: EventM (IORef [SomeAssignment])
214+class MonadIO m => Defer a m where
215+ getDeferralQueue :: m (IORef [a])
216+
217+{-# INLINE defer #-}
218+defer :: Defer a m => a -> m ()
219+defer a = do
220+ q <- getDeferralQueue
221+ liftIO $ modifyIORef' q (a:)
222+
223+instance Defer SomeAssignment (EventM x) where
224+ {-# INLINE getDeferralQueue #-}
124−askToAssignRef = EventM $ asks eventEnvAssignments
225+ getDeferralQueue = EventM $ asks eventEnvAssignments
125−
226+
126−askHoldInitRef :: EventM (IORef [SomeHoldInit])
227+instance Defer SomeHoldInit (EventM x) where
228+ {-# INLINE getDeferralQueue #-}
127−askHoldInitRef = EventM $ asks eventEnvHoldInits
229+ getDeferralQueue = EventM $ asks eventEnvHoldInits
128−
230+
231+instance Defer SomeHoldInit BehaviorM where
232+ {-# INLINE getDeferralQueue #-}
233+ getDeferralQueue = BehaviorM $ asks snd
234+
235+instance Defer SomeMergeUpdate (EventM x) where
236+ {-# INLINE getDeferralQueue #-}
237+ getDeferralQueue = EventM $ asks eventEnvMergeUpdates
238+
239+instance Defer SomeMergeInit (EventM x) where
240+ {-# INLINE getDeferralQueue #-}
241+ getDeferralQueue = EventM $ asks eventEnvMergeInits
242+
243+class HasCurrentHeight m where
129−getCurrentHeight :: EventM Int
244+ getCurrentHeight :: m Height
245+ scheduleMerge :: Height -> MergeSubscribed a -> m ()
246+
247+instance HasCurrentHeight (EventM x) where
248+ {-# INLINE getCurrentHeight #-}
130−getCurrentHeight = EventM $ do
249+ getCurrentHeight = EventM $ do
131− heightRef <- asks eventEnvCurrentHeight
250+ heightRef <- asks eventEnvCurrentHeight
132− liftIO $ readIORef heightRef
251+ liftIO $ readIORef heightRef
133−
252+ {-# INLINE scheduleMerge #-}
253+ scheduleMerge height subscribed = EventM $ do
254+ delayedRef <- asks eventEnvDelayedMerges
255+ liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [DelayedMerge subscribed]
256+
257+class HasSpiderEnv x m | m -> x where
258+ -- | Retrieve the current SpiderEnv
259+ askSpiderEnv :: m (SpiderEnv x)
260+
261+instance HasSpiderEnv x (EventM x) where
262+ {-# INLINE askSpiderEnv #-}
263+ askSpiderEnv = EventM $ asks eventEnvSpiderEnv
264+
265+instance HasSpiderEnv x (SpiderHost x) where
266+ {-# INLINE askSpiderEnv #-}
267+ askSpiderEnv = SpiderHost ask
268+
134−putCurrentHeight :: Int -> EventM ()
269+putCurrentHeight :: Height -> EventM x ()
135putCurrentHeight h = EventM $ do
270putCurrentHeight h = EventM $ do
136 heightRef <- asks eventEnvCurrentHeight
271 heightRef <- asks eventEnvCurrentHeight
137− liftIO $ writeIORef heightRef h
272+ liftIO $ writeIORef heightRef $! h
138
273
139−scheduleClear :: IORef (Maybe a) -> EventM ()
274+instance Defer SomeClear (EventM x) where
140−scheduleClear r = EventM $ do
275+ {-# INLINE getDeferralQueue #-}
141− clears <- asks eventEnvClears
276+ getDeferralQueue = EventM $ asks eventEnvClears
142− liftIO $ modifyIORef' clears (SomeMaybeIORef r :)
143
277
144−scheduleRootClear :: IORef (DMap k Identity) -> EventM ()
278+{-# INLINE scheduleClear #-}
145−scheduleRootClear r = EventM $ do
279+scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
146− clears <- asks eventEnvRootClears
280+scheduleClear r = defer $ SomeClear r
147− liftIO $ modifyIORef' clears (SomeDMapIORef r :)
148
281
149−scheduleMerge :: Int -> MergeSubscribed a -> EventM ()
282+instance Defer SomeRootClear (EventM x) where
150−scheduleMerge height subscribed = EventM $ do
283+ {-# INLINE getDeferralQueue #-}
151− delayedRef <- asks eventEnvDelayedMerges
284+ getDeferralQueue = EventM $ asks eventEnvRootClears
152− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) height [DelayedMerge subscribed]
153
285
154−emitCoincidenceInfo :: SomeCoincidenceInfo -> EventM ()
286+{-# INLINE scheduleRootClear #-}
155−emitCoincidenceInfo sci = EventM $ do
287+scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
156− ciRef <- asks eventEnvCoincidenceInfos
288+scheduleRootClear r = defer $ SomeRootClear r
157− liftIO $ modifyIORef' ciRef (sci:)
289+
290+instance Defer SomeResetCoincidence (EventM x) where
291+ {-# INLINE getDeferralQueue #-}
292+ getDeferralQueue = EventM $ asks eventEnvResetCoincidences
158
293
159-- Note: hold cannot examine its event until after the phase is over
294-- Note: hold cannot examine its event until after the phase is over
160−hold :: a -> Event a -> EventM (Behavior a)
295+{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> EventM x (Hold p a) #-}
296+{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> ComputeM (Hold p a) #-}
297+hold :: (R.Patch p, Defer SomeHoldInit m) => a -> Event (p a) -> m (Hold p a)
161hold v0 e = do
298hold v0 e = do
162− holdInitRef <- askHoldInitRef
163− liftIO $ do
164− valRef <- newIORef v0
299+ valRef <- liftIO $ newIORef v0
165− invsRef <- newIORef []
300+ invsRef <- liftIO $ newIORef []
166− parentRef <- newIORef $ error "hold not yet initialized (parent)"
301+ parentRef <- liftIO $ newIORef $ Left e
167− subscriberRef <- newIORef $ error "hold not yet initialized (subscriber)"
168− let h = Hold
169− { holdValue = valRef
170− , holdInvalidators = invsRef
171− , holdSubscriber = subscriberRef
172− , holdParent = parentRef
173#ifdef DEBUG_NODEIDS
302#ifdef DEBUG_NODEIDS
174− , holdNodeId = unsafeNodeId (v0, e)
303+ nodeId <- liftIO newNodeId
175#endif
304#endif
305+ let h = Hold
306+ { holdValue = valRef
307+ , holdInvalidators = invsRef
308+ , holdParent = parentRef
309+#ifdef DEBUG_NODEIDS
310+ , holdNodeId = nodeId
311+#endif
176− }
312+ }
313+ defer $ SomeHoldInit h
314+ return h
315+
316+{-# INLINE getHoldEventSubscription #-}
317+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, HasSpiderEnv x m) => Hold p a -> m (EventSubscription (p a))
318+getHoldEventSubscription h = do
319+ ep <- liftIO $ readIORef $ holdParent h
320+ case ep of
321+ Right subd -> return subd
322+ Left e -> do
323+ subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
177− s <- newSubscriberHold h
324+ subscription@(EventSubscription _ subd) <- subscribe e =<< liftIO (newSubscriberHold h)
178− writeIORef subscriberRef $ unsafeCoerce s
325+ liftIO $ writeIORef subscriptionRef $! subscription
179− modifyIORef' holdInitRef (SomeHoldInit e h :)
326+ occ <- liftIO $ readEventSubscribed subd
180− return $ BehaviorHold h
327+ case occ of
181−
328+ Nothing -> return ()
182−subscribeHold :: Event a -> Hold a -> EventM ()
329+ Just o -> do
183−subscribeHold e h = do
330+ old <- liftIO $ readIORef $ holdValue h
184− toAssignRef <- askToAssignRef
331+ case R.apply o old of
185− !s <- liftIO $ liftM unsafeCoerce $ readIORef $ holdSubscriber h -- This must be performed strictly so that the weak pointer points at the actual item
332+ Nothing -> return ()
186− ws <- liftIO $ mkWeakPtrWithDebug s "holdSubscriber"
333+ Just new -> do
187− subd <- subscribe e $ WeakSubscriberSimple ws
334+ -- Need to evaluate these so that we don't retain the Hold itself
188− liftIO $ writeIORef (holdParent h) $ unsafeCoerce subd
335+ v <- liftIO $ evaluate $ holdValue h
189− occ <- liftIO $ getEventSubscribedOcc subd
336+ i <- liftIO $ evaluate $ holdInvalidators h
190− case occ of
337+ defer $ SomeAssignment v i new
191− Nothing -> return ()
338+ liftIO $ writeIORef (holdParent h) $ Right subscription
192− Just o -> liftIO $ modifyIORef' toAssignRef (SomeAssignment h o :)
339+ return subscription
340+
341+type BehaviorEnv = (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]), IORef [SomeHoldInit])
193
342
194--type role BehaviorM representational
343--type role BehaviorM representational
195-- BehaviorM can sample behaviors
344-- BehaviorM can sample behaviors
196−newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed])) IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
345+newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT BehaviorEnv IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
346+
347+instance Monad BehaviorM where
348+ {-# INLINE (>>=) #-}
349+ BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
350+ {-# INLINE (>>) #-}
351+ BehaviorM x >> BehaviorM y = BehaviorM $ x >> y
352+ {-# INLINE return #-}
353+ return x = BehaviorM $ return x
354+ {-# INLINE fail #-}
355+ fail s = BehaviorM $ fail s
197
356
198data BehaviorSubscribed a
357data BehaviorSubscribed a
199− = BehaviorSubscribedHold (Hold a)
358+ = forall p. BehaviorSubscribedHold (Hold p a)
200 | BehaviorSubscribedPull (PullSubscribed a)
359 | BehaviorSubscribedPull (PullSubscribed a)
201
360
202data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
361data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
@@ -213,16 +372,26 @@ data PullSubscribed a
213data Pull a
372data Pull a
214 = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
373 = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
215 , pullCompute :: !(BehaviorM a)
374 , pullCompute :: !(BehaviorM a)
375+#ifdef DEBUG_NODEIDS
376+ , pullNodeId :: Int
377+#endif
216 }
378 }
217
379
218data Invalidator
380data Invalidator
219 = forall a. InvalidatorPull (Pull a)
381 = forall a. InvalidatorPull (Pull a)
220 | forall a. InvalidatorSwitch (SwitchSubscribed a)
382 | forall a. InvalidatorSwitch (SwitchSubscribed a)
221
383
222−data RootSubscribed a
384+data RootSubscribed a = forall k. GCompare k => RootSubscribed
385+ { rootSubscribedKey :: !(k a)
386+ , rootSubscribedCachedSubscribed :: !(IORef (DMap k RootSubscribed)) -- From the original Root
223− = RootSubscribed { rootSubscribedSubscribers :: !(IORef [WeakSubscriber a])
387+ , rootSubscribedSubscribers :: !(SubscriberList a)
224− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
388+ , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
389+ , rootSubscribedUninit :: IO ()
390+ , 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?
391+#ifdef DEBUG_NODEIDS
392+ , rootSubscribedNodeId :: Int
393+#endif
225− }
394+ }
226
395
227data Root (k :: * -> *)
396data Root (k :: * -> *)
228 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
397 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
@@ -230,35 +399,102 @@ data Root (k :: * -> *)
230 , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
399 , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
231 }
400 }
232
401
233−data SomeHoldInit = forall a. SomeHoldInit (Event a) (Hold a)
402+data SomeHoldInit = forall p a. R.Patch p => SomeHoldInit !(Hold p a)
403+
404+data SomeMergeUpdate = forall k. GCompare k => SomeMergeUpdate (R.PatchDMap (DMap k Event)) (MergeSubscribed k)
405+
406+data SomeMergeInit = forall k. GCompare k => SomeMergeInit !(MergeSubscribed k) !(Event (R.PatchDMap (DMap k Event)))
407+
408+data SomeEventSubscription = forall a. SomeEventSubscription (EventSubscription a)
234
409
235-- EventM can do everything BehaviorM can, plus create holds
410-- EventM can do everything BehaviorM can, plus create holds
236−newtype EventM a = EventM { unEventM :: ReaderT EventEnv IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException) -- The environment should be Nothing if we are not in a frame, and Just if we are - in which case it is a list of assignments to be done after the frame is over
411+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
237
412
238data PushSubscribed a b
413data PushSubscribed a b
414+ = PushSubscribed { pushSubscribedCachedSubscribed :: !(IORef (Maybe (PushSubscribed a b)))
239− = PushSubscribed { pushSubscribedOccurrence :: !(IORef (Maybe b)) -- If the current height is less than our height, this should always be Nothing; during our height, this will get filled in at some point, always before our children are notified; after our height, this will be filled in with the correct value (Nothing if we are not firing, Just if we are)
415+ , 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)
240− , pushSubscribedHeight :: !(IORef Int)
416+ , pushSubscribedHeight :: !(IORef Height)
241− , pushSubscribedSubscribers :: !(IORef [WeakSubscriber b])
417+ , pushSubscribedSubscribers :: !(SubscriberList b)
242− , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die
418+ , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die --TODO: Probably unneeded now
419+ , pushSubscribedWeakSelf :: !(IORef (Weak (PushSubscribed a b)))
243− , pushSubscribedParent :: !(EventSubscribed a)
420+ , pushSubscribedParent :: !(EventSubscription a)
244#ifdef DEBUG_NODEIDS
421#ifdef DEBUG_NODEIDS
245 , pushSubscribedNodeId :: Int
422 , pushSubscribedNodeId :: Int
246#endif
423#endif
247 }
424 }
248
425
426+newtype ComputeM a = ComputeM (ReaderT (IORef [SomeHoldInit]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
427+
428+instance Defer SomeHoldInit ComputeM where
429+ getDeferralQueue = ComputeM ask
430+
431+{-# SPECIALIZE runComputeM :: ComputeM a -> EventM x a #-}
432+runComputeM :: Defer SomeHoldInit m => ComputeM a -> m a
433+runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
434+
249data Push a b
435data Push a b
250− = Push { pushCompute :: !(a -> EventM (Maybe b)) -- Compute the current firing value; assumes that its parent has been computed
436+ = 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?)
251 , pushParent :: !(Event a)
437 , pushParent :: !(Event a)
252 , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
438 , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
253 }
439 }
254
440
441+data MergeSubscribedParent k a = MergeSubscribedParent !(EventSubscription a) !(Subscriber a)
442+
443+mergeSubscribedParentSubscription :: MergeSubscribedParent k a -> EventSubscription a
444+mergeSubscribedParentSubscription (MergeSubscribedParent es _) = es
445+
446+data HeightBag = HeightBag
447+ { _heightBag_size :: {-# UNPACK #-} !Int
448+ , _heightBag_contents :: !(IntMap Word) -- Number of excess in each bucket
449+ }
450+ deriving (Show, Read, Eq, Ord)
451+
452+heightBagEmpty :: HeightBag
453+heightBagEmpty = heightBagVerify $ HeightBag 0 IntMap.empty
454+
455+heightBagSize :: HeightBag -> Int
456+heightBagSize = _heightBag_size
457+
458+heightBagFromList :: [Height] -> HeightBag
459+heightBagFromList heights = heightBagVerify $ foldl' (flip heightBagAdd) heightBagEmpty heights
460+
461+heightBagAdd :: Height -> HeightBag -> HeightBag
462+heightBagAdd (Height h) (HeightBag s c) = heightBagVerify $ HeightBag (succ s) $ IntMap.insertWithKey (\_ _ old -> succ old) h 0 c
463+
464+heightBagRemove :: Height -> HeightBag -> HeightBag
465+heightBagRemove (Height h) b@(HeightBag s c) = heightBagVerify $ case IntMap.lookup h c of
466+ Nothing -> error $ "heightBagRemove: Height " <> show h <> " not present in bag " <> show b
467+ Just old -> HeightBag (pred s) $ case old of
468+ 0 -> IntMap.delete h c
469+ _ -> IntMap.insert h (pred old) c
470+
471+heightBagMax :: HeightBag -> Height
472+heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
473+ Just ((h, _), _) -> Height h
474+ Nothing -> zeroHeight
475+
476+heightBagVerify :: HeightBag -> HeightBag
477+#ifdef DEBUG
478+heightBagVerify b@(HeightBag s c) = if
479+ | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
480+ -> error $ "heightBagVerify: size doesn't match: " <> show b
481+ | unHeight invalidHeight `IntMap.member` c
482+ -> error $ "heightBagVerify: contains invalid height: " <> show b
483+ | otherwise -> b
484+#else
485+heightBagVerify = id
486+#endif
487+
255data MergeSubscribed k
488data MergeSubscribed k
489+ = MergeSubscribed { mergeSubscribedCachedSubscribed :: !(IORef (Maybe (MergeSubscribed k))) -- From the original merge
256− = MergeSubscribed { mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
490+ , mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
257 , mergeSubscribedAccum :: !(IORef (DMap k Identity)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
491 , mergeSubscribedAccum :: !(IORef (DMap k Identity)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
258− , mergeSubscribedHeight :: !(IORef Int)
492+ , mergeSubscribedHeight :: !(IORef Height)
493+ , mergeSubscribedHeightBag :: !(IORef HeightBag)
259− , mergeSubscribedSubscribers :: !(IORef [WeakSubscriber (DMap k Identity)])
494+ , mergeSubscribedSubscribers :: !(SubscriberList (DMap k Identity))
260− , mergeSubscribedSelf :: !Any -- Hold all our Subscribers in memory
495+ , mergeSubscribedChange :: !(IORef (Subscriber (R.PatchDMap (DMap k Event)), EventSubscribed (R.PatchDMap (DMap k Event))))
261− , mergeSubscribedParents :: !(DMap k EventSubscribed)
496+ , mergeSubscribedParents :: !(IORef (DMap k (MergeSubscribedParent k)))
497+ , mergeSubscribedWeakSelf :: !(IORef (Weak (MergeSubscribed k)))
262#ifdef DEBUG_NODEIDS
498#ifdef DEBUG_NODEIDS
263 , mergeSubscribedNodeId :: Int
499 , mergeSubscribedNodeId :: Int
264#endif
500#endif
@@ -266,28 +502,20 @@ data MergeSubscribed k
266
502
267--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
503--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
268data Merge k
504data Merge k
269− = Merge { mergeParents :: !(DMap k Event)
505+ = Merge { mergeParents :: !(Dynamic R.PatchDMap (DMap k Event))
270− , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k))) --TODO: Can we replace this with an unsafePerformIO thunk?
506+ , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k)))
271 }
507 }
272
508
273−data FanSubscriberKey k a where
509+data FanSubscribedChildren k a = FanSubscribedChildren
274− FanSubscriberKey :: k a -> FanSubscriberKey k [WeakSubscriber a]
510+ { _fanSubscribedChildren_list :: !(SubscriberList a)
275−
511+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed k)
276−instance GEq k => GEq (FanSubscriberKey k) where
512+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed k)))
277− geq (FanSubscriberKey a) (FanSubscriberKey b) = case geq a b of
513+ }
278− Nothing -> Nothing
279− Just Refl -> Just Refl
280−
281−instance GCompare k => GCompare (FanSubscriberKey k) where
282− gcompare (FanSubscriberKey a) (FanSubscriberKey b) = case gcompare a b of
283− GLT -> GLT
284− GEQ -> GEQ
285− GGT -> GGT
286
514
287data FanSubscribed k
515data FanSubscribed k
516+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed k)))
288− = FanSubscribed { fanSubscribedSubscribers :: !(IORef (DMap (FanSubscriberKey k) Identity))
517+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren k))) -- This DMap should never be empty
289− , fanSubscribedParent :: !(EventSubscribed (DMap k Identity))
518+ , fanSubscribedParent :: !(EventSubscription (DMap k Identity))
290− , fanSubscribedSelf :: {-# NOUNPACK #-} (Subscriber (DMap k Identity))
291#ifdef DEBUG_NODEIDS
519#ifdef DEBUG_NODEIDS
292 , fanSubscribedNodeId :: Int
520 , fanSubscribedNodeId :: Int
293#endif
521#endif
@@ -299,16 +527,16 @@ data Fan k
299 }
527 }
300
528
301data SwitchSubscribed a
529data SwitchSubscribed a
530+ = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
302− = SwitchSubscribed { switchSubscribedOccurrence :: !(IORef (Maybe a))
531+ , switchSubscribedOccurrence :: !(IORef (Maybe a))
303− , switchSubscribedHeight :: !(IORef Int)
532+ , switchSubscribedHeight :: !(IORef Height)
304− , switchSubscribedSubscribers :: !(IORef [WeakSubscriber a])
533+ , switchSubscribedSubscribers :: !(SubscriberList a)
305− , switchSubscribedSelf :: {-# NOUNPACK #-} (Subscriber a)
306− , switchSubscribedSelfWeak :: !(IORef (Weak (Subscriber a)))
307− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} Invalidator
534+ , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !Invalidator
308 , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
535 , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
309 , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
536 , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
310 , switchSubscribedParent :: !(Behavior (Event a))
537 , switchSubscribedParent :: !(Behavior (Event a))
311− , switchSubscribedCurrentParent :: !(IORef (EventSubscribed a))
538+ , switchSubscribedCurrentParent :: !(IORef (EventSubscription a))
539+ , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed a)))
312#ifdef DEBUG_NODEIDS
540#ifdef DEBUG_NODEIDS
313 , switchSubscribedNodeId :: Int
541 , switchSubscribedNodeId :: Int
314#endif
542#endif
@@ -319,13 +547,16 @@ data Switch a
319 , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
547 , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
320 }
548 }
321
549
550+{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
322data CoincidenceSubscribed a
551data CoincidenceSubscribed a
552+ = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
323− = CoincidenceSubscribed { coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
553+ , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
324− , coincidenceSubscribedSubscribers :: !(IORef [WeakSubscriber a])
554+ , coincidenceSubscribedSubscribers :: !(SubscriberList a)
325− , coincidenceSubscribedHeight :: !(IORef Int)
555+ , coincidenceSubscribedHeight :: !(IORef Height)
326 , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
556 , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
327− , coincidenceSubscribedOuterParent :: !(EventSubscribed (Event a))
557+ , coincidenceSubscribedOuterParent :: !(EventSubscription (Event a))
328 , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
558 , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
559+ , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed a)))
329#ifdef DEBUG_NODEIDS
560#ifdef DEBUG_NODEIDS
330 , coincidenceSubscribedNodeId :: Int
561 , coincidenceSubscribedNodeId :: Int
331#endif
562#endif
@@ -336,41 +567,17 @@ data Coincidence a
336 , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
567 , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
337 }
568 }
338
569
339−data Box a = Box { unBox :: a }
340−
341−--type WeakSubscriber a = Weak (Subscriber a)
342−data WeakSubscriber a
343− = forall k. GCompare k => WeakSubscriberMerge !(k a) !(Weak (Box (MergeSubscribed k))) --TODO: Can we inline the GCompare?
344− | WeakSubscriberSimple !(Weak (Subscriber a))
345−
346−showWeakSubscriberType :: WeakSubscriber a -> String
347−showWeakSubscriberType = \case
348− WeakSubscriberMerge _ _ -> "WeakSubscriberMerge"
349− WeakSubscriberSimple _ -> "WeakSubscriberSimple"
350−
351−deRefWeakSubscriber :: WeakSubscriber a -> IO (Maybe (Subscriber a))
352−deRefWeakSubscriber ws = case ws of
353− WeakSubscriberSimple w -> deRefWeak w
354− WeakSubscriberMerge k w -> liftM (fmap $ SubscriberMerge k . unBox) $ deRefWeak w
355−
356data Subscriber a
570data Subscriber a
357− = forall b. SubscriberPush !(a -> EventM (Maybe b)) (PushSubscribed a b)
571+ = forall b. SubscriberPush (a -> ComputeM (Maybe b)) (PushSubscribed a b) --TODO: Why does this have the pushCompute in it?
358 | forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k) --TODO: Can we inline the GCompare?
572 | forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k) --TODO: Can we inline the GCompare?
573+ | forall k. (a ~ R.PatchDMap (DMap k Event), GCompare k) => SubscriberMergeChange (MergeSubscribed k)
359 | forall k. (GCompare k, a ~ DMap k Identity) => SubscriberFan (FanSubscribed k)
574 | forall k. (GCompare k, a ~ DMap k Identity) => SubscriberFan (FanSubscribed k)
360− | SubscriberHold !(Hold a)
575+ | forall p b. (a ~ p b, R.Patch p) => SubscriberHold !(Hold p b)
576+ | forall b. (a ~ Identity b) => SubscriberHoldIdentity !(Hold Identity b)
361 | SubscriberSwitch (SwitchSubscribed a)
577 | SubscriberSwitch (SwitchSubscribed a)
362 | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
578 | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
363 | SubscriberCoincidenceInner (CoincidenceSubscribed a)
579 | SubscriberCoincidenceInner (CoincidenceSubscribed a)
364−
580+ | SubscriberHandle
365−showSubscriberType :: Subscriber a -> String
366−showSubscriberType = \case
367− SubscriberPush _ _ -> "SubscriberPush"
368− SubscriberMerge _ _ -> "SubscriberMerge"
369− SubscriberFan _ -> "SubscriberFan"
370− SubscriberHold _ -> "SubscriberHold"
371− SubscriberSwitch _ -> "SubscriberSwitch"
372− SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
373− SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
374
581
375data Event a
582data Event a
376 = forall k. GCompare k => EventRoot !(k a) !(Root k)
583 = forall k. GCompare k => EventRoot !(k a) !(Root k)
@@ -380,16 +587,15 @@ data Event a
380 | forall k. GCompare k => EventFan !(k a) !(Fan k)
587 | forall k. GCompare k => EventFan !(k a) !(Fan k)
381 | EventSwitch !(Switch a)
588 | EventSwitch !(Switch a)
382 | EventCoincidence !(Coincidence a)
589 | EventCoincidence !(Coincidence a)
383−
590+ | forall p b. (a ~ p b, R.Patch p) => EventHold !(Hold p b)
384−showEventType :: Event a -> String
591+ | forall p b. (a ~ p b, R.Patch p) => EventDyn !(Dyn p b)
385−showEventType = \case
592+ | forall b. (a ~ Identity b) => EventHoldIdentity !(Hold Identity b)
386− EventRoot _ _ -> "EventRoot"
593+ | forall b. (a ~ Identity b) => EventDynIdentity !(Dyn Identity b)
387− EventNever -> "EventNever"
594+
388− EventPush _ -> "EventPush"
595+data EventSubscription a = EventSubscription
389− EventMerge _ -> "EventMerge"
596+ { _eventSubscription_ticket :: !(Maybe (SubscriberListTicket a))
390− EventFan _ _ -> "EventFan"
597+ , _eventSubscription_subscribed :: !(EventSubscribed a)
391− EventSwitch _ -> "EventSwitch"
598+ }
392− EventCoincidence _ -> "EventCoincidence"
393
599
394data EventSubscribed a
600data EventSubscribed a
395 = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
601 = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
@@ -400,6 +606,9 @@ data EventSubscribed a
400 | EventSubscribedSwitch !(SwitchSubscribed a)
606 | EventSubscribedSwitch !(SwitchSubscribed a)
401 | EventSubscribedCoincidence !(CoincidenceSubscribed a)
607 | EventSubscribedCoincidence !(CoincidenceSubscribed a)
402
608
609+unsubscribe :: EventSubscription a -> IO ()
610+unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
611+
403-- These function are constructor functions that are marked NOINLINE so they are
612-- These function are constructor functions that are marked NOINLINE so they are
404-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
613-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
405-- so any weak references that are attached to the constructors will have their
614-- so any weak references that are attached to the constructors will have their
@@ -409,22 +618,30 @@ data EventSubscribed a
409-- The result is also evaluated to WHNF, since forcing a thunk invalidates
618-- The result is also evaluated to WHNF, since forcing a thunk invalidates
410-- the weak pointer to it in some cases.
619-- the weak pointer to it in some cases.
411
620
412−{-# NOINLINE newRootSubscribed #-}
621+--TODO: I think all of these noinlines are now unnecessary
413−newRootSubscribed :: IO (Maybe a) -> IORef [WeakSubscriber a] -> IO (RootSubscribed a)
414−newRootSubscribed occ subs =
415− return $! RootSubscribed
416− { rootSubscribedOccurrence = occ
417− , rootSubscribedSubscribers = subs
418− }
419−
420{-# NOINLINE newSubscriberPush #-}
622{-# NOINLINE newSubscriberPush #-}
421−newSubscriberPush :: (a -> EventM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
623+newSubscriberPush :: (a -> ComputeM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
422newSubscriberPush compute subd = return $! SubscriberPush compute subd
624newSubscriberPush compute subd = return $! SubscriberPush compute subd
423
625
626+{-# RULES
627+ "newSubscriberHold/Identity" forall h. newSubscriberHold h = newSubscriberHoldIdentity h
628+ #-}
424{-# NOINLINE newSubscriberHold #-}
629{-# NOINLINE newSubscriberHold #-}
425−newSubscriberHold :: Hold a -> IO (Subscriber a)
630+newSubscriberHold :: R.Patch p => Hold p a -> IO (Subscriber (p a))
426newSubscriberHold h = return $! SubscriberHold h
631newSubscriberHold h = return $! SubscriberHold h
427
632
633+{-# NOINLINE newSubscriberHoldIdentity #-}
634+newSubscriberHoldIdentity :: Hold Identity a -> IO (Subscriber (Identity a))
635+newSubscriberHoldIdentity h = return $! SubscriberHoldIdentity h
636+
637+{-# NOINLINE newSubscriberMerge #-}
638+newSubscriberMerge :: GCompare k => k a -> MergeSubscribed k -> IO (Subscriber a)
639+newSubscriberMerge k subd = return $! SubscriberMerge k subd
640+
641+{-# NOINLINE newSubscriberMergeChange #-}
642+newSubscriberMergeChange :: GCompare k => MergeSubscribed k -> IO (Subscriber (R.PatchDMap (DMap k Event)))
643+newSubscriberMergeChange subd = return $! SubscriberMergeChange subd
644+
428{-# NOINLINE newSubscriberFan #-}
645{-# NOINLINE newSubscriberFan #-}
429newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
646newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
430newSubscriberFan subd = return $! SubscriberFan subd
647newSubscriberFan subd = return $! SubscriberFan subd
@@ -449,15 +666,100 @@ newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
449newInvalidatorPull :: Pull a -> IO Invalidator
666newInvalidatorPull :: Pull a -> IO Invalidator
450newInvalidatorPull p = return $! InvalidatorPull p
667newInvalidatorPull p = return $! InvalidatorPull p
451
668
452−{-# NOINLINE newBox #-}
669+data Dynamic p a
453−newBox :: a -> IO (Box a)
670+ = DynamicHold !(Hold p a)
454−newBox a = return $! Box a
671+ | p ~ Identity => DynamicHoldIdentity !(Hold Identity a)
672+ | DynamicConst !a
673+ | DynamicDyn !(Dyn p a)
674+ | p ~ Identity => DynamicDynIdentity !(Dyn Identity a)
675+
676+{-# INLINE current #-}
677+current :: R.Patch p => Dynamic p a -> Behavior a
678+current = \case
679+ DynamicHold h -> BehaviorHold h
680+ DynamicHoldIdentity h -> BehaviorHoldIdentity h
681+ DynamicConst a -> BehaviorConst a
682+ DynamicDyn d -> BehaviorDyn d
683+ DynamicDynIdentity d -> BehaviorDynIdentity d
684+
685+--TODO: If you only need updated, not current, can we avoid actually constructing the Hold?
686+{-# INLINE updated #-}
687+updated :: R.Patch p => Dynamic p a -> Event (p a)
688+updated = \case
689+ DynamicHold h -> EventHold h
690+ DynamicHoldIdentity h -> EventHoldIdentity h
691+ DynamicConst _ -> EventNever
692+ DynamicDyn d -> EventDyn d
693+ DynamicDynIdentity d -> EventDynIdentity d
694+
695+newMapDyn :: (a -> b) -> Dynamic Identity a -> Dynamic Identity b
696+newMapDyn f d = DynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ current d) (Identity . f . runIdentity <$> updated d)
697+
698+instance Functor (Dynamic Identity) where
699+ fmap = newMapDyn
700+
701+instance Applicative (Dynamic Identity) where
702+ pure = DynamicConst
703+ (<*>) = zipDynWith ($)
704+ _ *> b = b
705+ a <* _ = a
706+
707+instance R.FunctorMaybe Event where
708+ fmapMaybe f = push $ return . f
709+
710+instance Align Event where
711+ nil = EventNever
712+ align ea eb = R.fmapMaybe R.dmapToThese $ merge $ DynamicConst $ DMap.fromList [R.LeftTag :=> ea, R.RightTag :=> eb]
713+
714+--TODO: Avoid the duplication between this and R.zipDynWith
715+zipDynWith :: (a -> b -> c) -> Dynamic Identity a -> Dynamic Identity b -> Dynamic Identity c
716+zipDynWith f da db =
717+ let eab = align (updated da) (updated db)
718+ ec = flip push eab $ \o -> do
719+ (a, b) <- case o of
720+ This (Identity a) -> do
721+ b <- readBehaviorUntracked $ current db
722+ return (a, b)
723+ That (Identity b) -> do
724+ a <- readBehaviorUntracked $ current da
725+ return (a, b)
726+ These (Identity a) (Identity b) -> return (a, b)
727+ return $ Just $ Identity $ f a b
728+ in DynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (current da) <*> readBehaviorUntracked (current db)) ec
729+
730+instance Monad (Dynamic Identity) where
731+ {-# INLINE return #-}
732+ return = pure
733+ {-# INLINE (>>=) #-}
734+ x >>= f = DynamicDynIdentity $ newJoinDyn $ newMapDyn f x --TODO: (>>), fail
735+ {-# INLINE (>>) #-}
736+ _ >> y = y
737+ {-# INLINE fail #-}
738+ fail _ = error "Dynamic does not support 'fail'"
739+
740+newtype Dyn p a = Dyn { unDyn :: IORef (Either (BehaviorM a, Event (p a)) (Hold p a)) }
741+
742+unsafeDyn :: BehaviorM a -> Event (p a) -> Dyn p a
743+unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
744+ where x = (readV0, v')
745+
746+newJoinDyn :: Dynamic Identity (Dynamic Identity a) -> Dyn Identity a
747+newJoinDyn d =
748+ let readV0 = readBehaviorTracked . current =<< readBehaviorTracked (current d)
749+ eOuter = push (fmap (Just . Identity) . readBehaviorUntracked . current . runIdentity) $ updated d
750+ eInner = switch $ updated <$> current d
751+ eBoth = coincidence $ updated . runIdentity <$> updated d
752+ v' = unSpiderEvent $ R.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
753+ in unsafeDyn readV0 v'
455
754
456--type role Behavior representational
755--type role Behavior representational
457data Behavior a
756data Behavior a
458− = BehaviorHold !(Hold a)
757+ = forall p. BehaviorHold !(Hold p a)
758+ | BehaviorHoldIdentity !(Hold Identity a)
459 | BehaviorConst !a
759 | BehaviorConst !a
460 | BehaviorPull !(Pull a)
760 | BehaviorPull !(Pull a)
761+ | BehaviorDynIdentity !(Dyn Identity a)
762+ | forall p. R.Patch p => BehaviorDyn !(Dyn p a)
461
763
462-- ResultM can read behaviors and events
764-- ResultM can read behaviors and events
463type ResultM = EventM
765type ResultM = EventM
@@ -470,24 +772,26 @@ instance Functor Event where
470 fmap f = push $ return . Just . f
772 fmap f = push $ return . Just . f
471
773
472instance Functor Behavior where
774instance Functor Behavior where
473− fmap f = pull . liftM f . readBehaviorTracked
775+ fmap f = pull . fmap f . readBehaviorTracked
474
776
475{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
777{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
476−push :: (a -> EventM (Maybe b)) -> Event a -> Event b
778+push :: forall a b. (a -> ComputeM (Maybe b)) -> Event a -> Event b
477push f e = EventPush $ Push
779push f e = EventPush $ Push
478 { pushCompute = f
780 { pushCompute = f
479 , pushParent = e
781 , pushParent = e
480 , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
782 , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
481 }
783 }
482−{-# RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
784+-- DISABLED: {- RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
483
785
484{-# NOINLINE pull #-}
786{-# NOINLINE pull #-}
485pull :: BehaviorM a -> Behavior a
787pull :: BehaviorM a -> Behavior a
486pull a = BehaviorPull $ Pull
788pull a = BehaviorPull $ Pull
487 { pullCompute = a
789 { pullCompute = a
488 , pullValue = unsafeNewIORef a Nothing
790 , pullValue = unsafeNewIORef a Nothing
791+#ifdef DEBUG_NODEIDS
792+ , pullNodeId = unsafeNodeId a
793+#endif
489 }
794 }
490−{-# RULES "pull/pull" forall a. pull (readBehaviorTracked (pull a)) = pull a #-}
491
795
492{-# NOINLINE switch #-}
796{-# NOINLINE switch #-}
493switch :: Behavior (Event a) -> Event a
797switch :: Behavior (Event a) -> Event a
@@ -495,7 +799,6 @@ switch a = EventSwitch $ Switch
495 { switchParent = a
799 { switchParent = a
496 , switchSubscribed = unsafeNewIORef a Nothing
800 , switchSubscribed = unsafeNewIORef a Nothing
497 }
801 }
498−{-# RULES "switch/constB" forall e. switch (BehaviorConst e) = e #-}
499
802
500coincidence :: Event (Event a) -> Event a
803coincidence :: Event (Event a) -> Event a
501coincidence a = EventCoincidence $ Coincidence
804coincidence a = EventCoincidence $ Coincidence
@@ -503,32 +806,20 @@ coincidence a = EventCoincidence $ Coincidence
503 , coincidenceSubscribed = unsafeNewIORef a Nothing
806 , coincidenceSubscribed = unsafeNewIORef a Nothing
504 }
807 }
505
808
506−newRoot :: IO (Root k)
507−newRoot = do
508− occRef <- newIORef DMap.empty
509− subscribedRef <- newIORef DMap.empty
510− return $ Root
511− { rootOccurrence = occRef
512− , rootSubscribed = subscribedRef
513− , rootInit = \_ _ -> return $ return ()
514− }
515−
516−propagateAndUpdateSubscribersRef :: IORef [WeakSubscriber a] -> a -> EventM ()
809+propagateAndUpdateSubscribersRef :: SubscriberList a -> a -> EventM x ()
517propagateAndUpdateSubscribersRef subscribersRef a = do
810propagateAndUpdateSubscribersRef subscribersRef a = do
518− subscribers <- liftIO $ readIORef subscribersRef
519− liftIO $ writeIORef subscribersRef []
520− stillAlive <- propagate a subscribers
521− liftIO $ modifyIORef' subscribersRef (++stillAlive)
811+ propagate a subscribersRef
522
812
523-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
813-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
524−run :: [DSum RootTrigger Identity] -> ResultM b -> IO b
814+run :: [DSum RootTrigger Identity] -> ResultM x b -> SpiderHost x b
525run roots after = do
815run roots after = do
526− when debugPropagate $ putStrLn "Running an event frame"
816+ tracePropagate $ "Running an event frame with " <> show (length roots) <> " events"
527− result <- runFrame $ do
817+ spiderEnv <- SpiderHost ask
818+ result <- SpiderHost $ lift $ withMVar (_spiderEnv_lock spiderEnv) $ \_ -> flip runReaderT spiderEnv $ unSpiderHost $ runFrame $ do
528 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
819 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
529 occBefore <- liftIO $ do
820 occBefore <- liftIO $ do
530 occBefore <- readIORef occRef
821 occBefore <- readIORef occRef
531− writeIORef occRef $ DMap.insert k a occBefore
822+ writeIORef occRef $! DMap.insert k a occBefore
532 return occBefore
823 return occBefore
533 if DMap.null occBefore
824 if DMap.null occBefore
534 then do scheduleRootClear occRef
825 then do scheduleRootClear occRef
@@ -541,19 +832,19 @@ run roots after = do
541 delayed <- liftIO $ readIORef delayedRef
832 delayed <- liftIO $ readIORef delayedRef
542 case IntMap.minViewWithKey delayed of
833 case IntMap.minViewWithKey delayed of
543 Nothing -> return ()
834 Nothing -> return ()
544− Just ((currentHeight, current), future) -> do
835+ Just ((currentHeight, cur), future) -> do
545− when debugPropagate $ liftIO $ putStrLn $ "Running height " ++ show currentHeight
836+ tracePropagate $ "Running height " ++ show currentHeight
546− putCurrentHeight currentHeight
837+ putCurrentHeight $ Height currentHeight
547− liftIO $ writeIORef delayedRef future
838+ liftIO $ writeIORef delayedRef $! future
548− forM_ current $ \d -> case d of
839+ forM_ cur $ \d -> case d of
549 DelayedMerge subscribed -> do
840 DelayedMerge subscribed -> do
550 height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
841 height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
551− case height `compare` currentHeight of
842+ case height `compare` Height currentHeight of
552 LT -> error "Somehow a merge's height has been decreased after it was scheduled"
843 LT -> error "Somehow a merge's height has been decreased after it was scheduled"
553 GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
844 GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
554 EQ -> do
845 EQ -> do
555 m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
846 m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
556− liftIO $ writeIORef (mergeSubscribedAccum subscribed) DMap.empty
847+ liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! DMap.empty
557 --TODO: Assert that m is not empty
848 --TODO: Assert that m is not empty
558 liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
849 liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
559 scheduleClear $ mergeSubscribedOccurrence subscribed
850 scheduleClear $ mergeSubscribedOccurrence subscribed
@@ -562,14 +853,14 @@ run roots after = do
562 go
853 go
563 putCurrentHeight maxBound
854 putCurrentHeight maxBound
564 after
855 after
565− when debugPropagate $ putStrLn "Done running an event frame"
856+ tracePropagate "Done running an event frame"
566 return result
857 return result
567
858
568−data SomeMaybeIORef = forall a. SomeMaybeIORef (IORef (Maybe a))
859+data SomeClear = forall a. SomeClear (IORef (Maybe a))
569
860
570−data SomeDMapIORef = forall k. SomeDMapIORef (IORef (DMap k Identity))
861+data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
571
862
572−data SomeAssignment = forall a. SomeAssignment (Hold a) a
863+data SomeAssignment = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak Invalidator]) a
573
864
574data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
865data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
575
866
@@ -577,136 +868,241 @@ debugFinalize :: Bool
577debugFinalize = False
868debugFinalize = False
578
869
579mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
870mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
580−mkWeakPtrWithDebug x debugNote = mkWeakPtr x $
871+mkWeakPtrWithDebug x debugNote = do
872+ x' <- evaluate x
873+ mkWeakPtr x' $
581− if debugFinalize
874+ if debugFinalize
582− then Just $ putStrLn $ "finalizing: " ++ debugNote
875+ then Just $ putStrLn $ "finalizing: " ++ debugNote
583− else Nothing
876+ else Nothing
584
877
585type WeakList a = [Weak a]
878type WeakList a = [Weak a]
586
879
587−--TODO: Is it faster to clean up every time, or to occasionally go through and clean up as needed?
880+{-# INLINE withIncreasedDepth #-}
588−traverseAndCleanWeakList_ :: Monad m => (wa -> m (Maybe a)) -> [wa] -> (a -> m ()) -> m [wa]
881+#ifdef DEBUG
589−traverseAndCleanWeakList_ deRef ws f = go ws
882+withIncreasedDepth :: CanTrace x m => m a -> m a
590− where go [] = return []
883+withIncreasedDepth a = do
591− go (h:t) = do
884+ spiderEnv <- askSpiderEnv
592− ma <- deRef h
885+ liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) succ
593− case ma of
886+ result <- a
594− Just a -> do
887+ liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) pred
595− f a
888+ return result
596− t' <- go t
889+#else
597− return $ h : t'
890+withIncreasedDepth :: m a -> m a
598− Nothing -> go t
891+withIncreasedDepth = id
892+#endif
893+
894+type CanTrace x m = (HasSpiderEnv x m, MonadIO m)
895+
896+{-# INLINE tracePropagate #-}
897+tracePropagate :: (CanTrace x m) => String -> m ()
898+tracePropagate = traceWhen debugPropagate
899+
900+{-# INLINE traceSubscribe #-}
901+traceSubscribe :: String -> IO ()
902+traceSubscribe = when debugSubscribe . liftIO . putStrLn
903+
904+{-# INLINE traceInvalidate #-}
905+traceInvalidate :: String -> IO ()
906+traceInvalidate = when debugInvalidate . liftIO . putStrLn
907+
908+{-# INLINE traceWhen #-}
909+traceWhen :: (CanTrace x m) => Bool -> String -> m ()
910+traceWhen b message = traceMWhen b $ return message
911+
912+{-# INLINE traceMWhen #-}
913+traceMWhen :: (CanTrace x m) => Bool -> m String -> m ()
914+traceMWhen b getMessage = when b $ do
915+ message <- getMessage
916+#ifdef DEBUG
917+ spiderEnv <- askSpiderEnv
918+ d <- liftIO $ readIORef $ _spiderEnv_depth spiderEnv
919+#else
920+ let d = 0
921+#endif
922+ liftIO $ putStrLn $ replicate d ' ' <> message
923+
924+whoCreatedIORef :: IORef a -> IO [String]
925+whoCreatedIORef (IORef a) = whoCreated $! a
926+
927+whoCreatedEventSubscribed :: EventSubscribed a -> IO [String]
928+whoCreatedEventSubscribed = \case
929+ EventSubscribedRoot _ -> return ["root"]
930+ EventSubscribedNever -> return ["never"]
931+ EventSubscribedPush subscribed -> whoCreatedIORef $ pushSubscribedCachedSubscribed subscribed
932+ EventSubscribedFan _ subscribed -> whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
933+ EventSubscribedMerge subscribed -> whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
934+ EventSubscribedSwitch subscribed -> whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
935+ EventSubscribedCoincidence subscribed -> whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
936+
937+walkInvalidHeightParents :: EventSubscribed a -> IO [Some EventSubscribed]
938+walkInvalidHeightParents s0 = do
939+ subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
940+ h <- liftIO $ readIORef $ eventSubscribedHeightRef s
941+ when (h == invalidHeight) $ do
942+ when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
943+ modify (Some.This s :)
944+ mapM_ loop =<< liftIO (eventSubscribedGetParents s)
945+ forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
946+ return subscribers
947+
948+groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
949+groupByHead = \case
950+ [] -> []
951+ (x :| xs) : t -> case groupByHead t of
952+ [] -> [(x, xs :| [])]
953+ l@((y, yss) : t')
954+ | x == y -> (x, xs `NonEmpty.cons` yss) : t'
955+ | otherwise -> (x, xs :| []) : l
956+
957+listsToForest :: Eq a => [[a]] -> Forest a
958+listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
599
959
600-- | Propagate everything at the current height
960-- | Propagate everything at the current height
601−propagate :: a -> [WeakSubscriber a] -> EventM [WeakSubscriber a]
961+propagate :: a -> SubscriberList a -> EventM x ()
602−propagate a subscribers = do
962+propagate a subscribers = withIncreasedDepth $ do
603− traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subscribers $ \s -> case s of
963+ -- 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
964+ --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
965+ WeakBag.traverse subscribers $ \s -> case s of
604− SubscriberPush compute subscribed -> do
966+ SubscriberPush compute subscribed -> {-# SCC "traversePush" #-} do
605− when debugPropagate $ liftIO $ putStrLn $ "SubscriberPush" <> showNodeId subscribed
967+ tracePropagate $ "SubscriberPush" <> showNodeId subscribed
606− occ <- compute a
968+ occ <- runComputeM $ compute a
607 case occ of
969 case occ of
608 Nothing -> return () -- No need to write a Nothing back into the Ref
970 Nothing -> return () -- No need to write a Nothing back into the Ref
609 Just o -> do
971 Just o -> do
610 liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
972 liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
611 scheduleClear $ pushSubscribedOccurrence subscribed
973 scheduleClear $ pushSubscribedOccurrence subscribed
612− liftIO . writeIORef (pushSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (pushSubscribedSubscribers subscribed))
974+ propagate o $ pushSubscribedSubscribers subscribed
613− SubscriberMerge k subscribed -> do
975+ SubscriberMerge k subscribed -> {-# SCC "traverseMerge" #-} do
614− when debugPropagate $ liftIO $ putStrLn $ "SubscriberMerge" <> showNodeId subscribed
976+ tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
615 oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
977 oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
616− liftIO $ writeIORef (mergeSubscribedAccum subscribed) $ DMap.insertWith (error "Same key fired multiple times for") k (Identity a) oldM
978+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge" <> showNodeId subscribed) k (Identity a) oldM
979+ tracePropagate $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
980+ liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! newM
617 when (DMap.null oldM) $ do -- Only schedule the firing once
981 when (DMap.null oldM) $ do -- Only schedule the firing once
618 height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
982 height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
619 --TODO: assertions about height
983 --TODO: assertions about height
620 currentHeight <- getCurrentHeight
984 currentHeight <- getCurrentHeight
985+ when (height <= currentHeight) $ do
986+ myStack <- liftIO $ whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
987+ if height /= invalidHeight
621− when (height <= currentHeight) $ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")"
988+ then error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
989+ else liftIO $ do
990+ nodesInvolvedInCycle <- walkInvalidHeightParents $ EventSubscribedMerge subscribed
991+ stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
992+ error $ "Causality loop found:\n" <> drawForest (listsToForest stacks)
622 scheduleMerge height subscribed
993 scheduleMerge height subscribed
994+ SubscriberMergeChange subscribed -> {-# SCC "traverseMergeChange" #-} do
995+ tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
996+ defer $ SomeMergeUpdate a subscribed
623− SubscriberFan subscribed -> do
997+ SubscriberFan subscribed -> {-# SCC "traverseFan" #-} do
624 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
998 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
625− when debugPropagate $ liftIO $ putStrLn $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
999+ tracePropagate $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
626 --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
1000 --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
627− forM_ (DMap.toList a) $ \(k :=> Identity v) -> case DMap.lookup (FanSubscriberKey k) subs of
1001+ forM_ (DMap.toList a) $ \(k :=> Identity v) -> {-# SCC "SubscriberFan" #-} case DMap.lookup k subs of
628 Nothing -> do
1002 Nothing -> do
629− when debugPropagate $ liftIO $ putStrLn "No subscriber for key"
1003+ tracePropagate "No subscriber for key"
630 return ()
1004 return ()
631− Just (Identity subsubs) -> do
1005+ Just subsubs -> do
632− _ <- propagate v subsubs --TODO: use the value of this
1006+ propagate v $ _fanSubscribedChildren_list subsubs
633 return ()
1007 return ()
634− --TODO: The following is way too slow to do all the time
635− subs' <- liftIO $ forM (DMap.toList subs) $ ((\(FanSubscriberKey k :=> Identity subsubs) -> do
636− subsubs' <- traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subsubs (const $ return ())
637− return $ if null subsubs'
638− then Nothing
639− else Just $ FanSubscriberKey k :=> Identity subsubs') :: DSum (FanSubscriberKey k) Identity -> IO (Maybe (DSum (FanSubscriberKey k) Identity)))
640− liftIO $ writeIORef (fanSubscribedSubscribers subscribed) $ DMap.fromDistinctAscList $ catMaybes subs'
641− SubscriberHold h -> do
1008+ SubscriberHold h -> {-# SCC "traverseHold" #-} propagateSubscriberHold h a
642− invalidators <- liftIO $ readIORef $ holdInvalidators h
1009+ SubscriberHoldIdentity h -> {-# SCC "traverseHoldIdentity" #-} propagateSubscriberHold h a
643− when debugPropagate $ liftIO $ putStrLn $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
644− toAssignRef <- askToAssignRef
645− liftIO $ modifyIORef' toAssignRef (SomeAssignment h a :)
646− SubscriberSwitch subscribed -> do
1010+ SubscriberSwitch subscribed -> {-# SCC "traverseSwitch" #-} do
647− when debugPropagate $ liftIO $ putStrLn $ "SubscriberSwitch" <> showNodeId subscribed
1011+ tracePropagate $ "SubscriberSwitch" <> showNodeId subscribed
648 liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
1012 liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
649 scheduleClear $ switchSubscribedOccurrence subscribed
1013 scheduleClear $ switchSubscribedOccurrence subscribed
650− subs <- liftIO $ readIORef $ switchSubscribedSubscribers subscribed
1014+ propagate a $ switchSubscribedSubscribers subscribed
651− liftIO . writeIORef (switchSubscribedSubscribers subscribed) =<< propagate a subs
652− SubscriberCoincidenceOuter subscribed -> do
1015+ SubscriberCoincidenceOuter subscribed -> {-# SCC "traverseCoincidenceOuter" #-} do
653− when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
1016+ tracePropagate $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
654 outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
1017 outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
655− when debugPropagate $ liftIO $ putStrLn $ " outerHeight = " <> show outerHeight
1018+ tracePropagate $ " outerHeight = " <> show outerHeight
656 (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
1019 (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
657− when debugPropagate $ liftIO $ putStrLn $ " isJust occ = " <> show (isJust occ)
1020+ tracePropagate $ " isJust occ = " <> show (isJust occ)
658− when debugPropagate $ liftIO $ putStrLn $ " innerHeight = " <> show innerHeight
1021+ tracePropagate $ " innerHeight = " <> show innerHeight
659 liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
1022 liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
660 scheduleClear $ coincidenceSubscribedInnerParent subscribed
1023 scheduleClear $ coincidenceSubscribedInnerParent subscribed
661 case occ of
1024 case occ of
662 Nothing -> do
1025 Nothing -> do
663 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
1026 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
664− writeIORef (coincidenceSubscribedHeight subscribed) innerHeight
1027+ writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
1028+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
665− mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
1029+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
666− mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
667 Just o -> do -- Since it's already firing, no need to adjust height
1030 Just o -> do -- Since it's already firing, no need to adjust height
668 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
1031 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
669 scheduleClear $ coincidenceSubscribedOccurrence subscribed
1032 scheduleClear $ coincidenceSubscribedOccurrence subscribed
670− liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
1033+ propagate o $ coincidenceSubscribedSubscribers subscribed
671− SubscriberCoincidenceInner subscribed -> do
1034+ SubscriberCoincidenceInner subscribed -> {-# SCC "traverseCoincidenceInner" #-} do
672− when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceInner" <> showNodeId subscribed
1035+ tracePropagate $ "SubscriberCoincidenceInner" <> showNodeId subscribed
1036+ occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
1037+ case occ of
1038+ Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
1039+ Nothing -> do
673− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
1040+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
674− scheduleClear $ coincidenceSubscribedOccurrence subscribed
1041+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
675− liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate a =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
1042+ propagate a $ coincidenceSubscribedSubscribers subscribed
1043+ SubscriberHandle -> {-# SCC "traverseHandle" #-} tracePropagate "SubscriberHandle"
1044+
1045+{-# INLINE propagateSubscriberHold #-}
1046+propagateSubscriberHold :: R.Patch p => Hold p a -> p a -> EventM x ()
1047+propagateSubscriberHold h a = do
1048+ {-# SCC "trace" #-} traceMWhen debugPropagate $ liftIO $ do
1049+ invalidators <- liftIO $ readIORef $ holdInvalidators h
1050+ return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1051+ v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1052+ case {-# SCC "apply" #-} R.apply a v of
1053+ Nothing -> return ()
1054+ Just v' -> do
1055+ {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1056+ vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1057+ iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1058+ defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
676
1059
677−data SomeCoincidenceInfo = forall a. SomeCoincidenceInfo (Weak (Subscriber a)) (Subscriber a) (Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1060+data SomeResetCoincidence = forall a. SomeResetCoincidence !(EventSubscription a) !(Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
678
1061
1062+{-# INLINE subscribeCoincidenceInner #-}
679−subscribeCoincidenceInner :: Event a -> Int -> CoincidenceSubscribed a -> EventM (Maybe a, Int, EventSubscribed a)
1063+subscribeCoincidenceInner :: CanSubscribe x m => Event a -> Height -> CoincidenceSubscribed a -> m (Maybe a, Height, EventSubscribed a)
680subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
1064subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
681 subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
1065 subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
682− wsubInner <- liftIO $ mkWeakPtrWithDebug subInner "SubscriberCoincidenceInner"
1066+ subscription@(EventSubscription _ innerSubd) <- subscribe o subInner
683− innerSubd <- {-# SCC "innerSubd" #-} (subscribe o $ WeakSubscriberSimple wsubInner)
684− innerOcc <- liftIO $ getEventSubscribedOcc innerSubd
1067+ innerOcc <- liftIO $ readEventSubscribed innerSubd
685 innerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef innerSubd
1068 innerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef innerSubd
686 let height = max innerHeight outerHeight
1069 let height = max innerHeight outerHeight
687− emitCoincidenceInfo $ SomeCoincidenceInfo wsubInner subInner $ if height > outerHeight then Just subscribedUnsafe else Nothing
1070+ defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
688 return (innerOcc, height, innerSubd)
1071 return (innerOcc, height, innerSubd)
689
1072
690−readBehavior :: Behavior a -> IO a
1073+{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> BehaviorM a #-}
1074+{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> EventM x a #-}
1075+readBehaviorUntracked :: Defer SomeHoldInit m => Behavior a -> m a
1076+readBehaviorUntracked b = do
1077+ holdInits <- getDeferralQueue
691−readBehavior b = runBehaviorM (readBehaviorTracked b) Nothing --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
1078+ liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
692
1079
693−runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IO a
1080+runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IORef [SomeHoldInit] -> IO a
694−runBehaviorM a mwi = runReaderT (unBehaviorM a) mwi
1081+runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
695
1082
696askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
1083askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
697−askInvalidator = liftM (fmap fst) $ BehaviorM ask
1084+askInvalidator = do
1085+ (!m, _) <- BehaviorM ask
1086+ case m of
1087+ Nothing -> return Nothing
1088+ Just (!wi, _) -> return $ Just wi
698
1089
699askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
1090askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
700−askParentsRef = liftM (fmap snd) $ BehaviorM ask
1091+askParentsRef = do
1092+ (!m, _) <- BehaviorM ask
1093+ case m of
1094+ Nothing -> return Nothing
1095+ Just (_, !p) -> return $ Just p
1096+
1097+askBehaviorHoldInits :: BehaviorM (IORef [SomeHoldInit])
1098+askBehaviorHoldInits = do
1099+ (_, !his) <- BehaviorM ask
1100+ return his
701
1101
702readBehaviorTracked :: Behavior a -> BehaviorM a
1102readBehaviorTracked :: Behavior a -> BehaviorM a
703readBehaviorTracked b = case b of
1103readBehaviorTracked b = case b of
704− BehaviorHold h -> do
1104+ BehaviorHold h -> readHoldTracked h
705− result <- liftIO $ readIORef $ holdValue h
1105+ BehaviorHoldIdentity h -> readHoldTracked h
706− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
707− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
708− liftIO $ touch $ holdSubscriber h
709− return result
710 BehaviorConst a -> return a
1106 BehaviorConst a -> return a
711 BehaviorPull p -> do
1107 BehaviorPull p -> do
712 val <- liftIO $ readIORef $ pullValue p
1108 val <- liftIO $ readIORef $ pullValue p
@@ -720,7 +1116,8 @@ readBehaviorTracked b = case b of
720 i <- liftIO $ newInvalidatorPull p
1116 i <- liftIO $ newInvalidatorPull p
721 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
1117 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
722 parentsRef <- liftIO $ newIORef []
1118 parentsRef <- liftIO $ newIORef []
1119+ holdInits <- askBehaviorHoldInits
723− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
1120+ a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
724 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
1121 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
725 parents <- liftIO $ readIORef parentsRef
1122 parents <- liftIO $ readIORef parentsRef
726 let subscribed = PullSubscribed
1123 let subscribed = PullSubscribed
@@ -732,299 +1129,492 @@ readBehaviorTracked b = case b of
732 liftIO $ writeIORef (pullValue p) $ Just subscribed
1129 liftIO $ writeIORef (pullValue p) $ Just subscribed
733 askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
1130 askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
734 return a
1131 return a
1132+ BehaviorDyn d -> readHoldTracked =<< getDynHold d
1133+ BehaviorDynIdentity d -> readHoldTracked =<< getDynHold d
1134+
1135+{-# INLINE readHoldTracked #-}
1136+readHoldTracked :: Hold p a -> BehaviorM a
1137+readHoldTracked h = do
1138+ result <- liftIO $ readIORef $ holdValue h
1139+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
1140+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
1141+ return result
1142+
1143+{-# INLINE getDynHold #-}
1144+getDynHold :: (Defer SomeHoldInit m, R.Patch p) => Dyn p a -> m (Hold p a)
1145+getDynHold d = do
1146+ mh <- liftIO $ readIORef $ unDyn d
1147+ case mh of
1148+ Right h -> return h
1149+ Left (readV0, v') -> do
1150+ holdInits <- getDeferralQueue
1151+ v0 <- liftIO $ runBehaviorM readV0 Nothing holdInits
1152+ h <- hold v0 v'
1153+ liftIO $ writeIORef (unDyn d) $ Right h
1154+ return h
735
1155
1156+{-
736readEvent :: Event a -> ResultM (Maybe a)
1157readEvent :: Event a -> ResultM (Maybe a)
737readEvent e = case e of
1158readEvent e = case e of
738− EventRoot k r -> liftIO . liftM (coerce . DMap.lookup k) . readIORef $ rootOccurrence r
1159+ EventRoot k r -> liftIO . fmap (coerce . DMap.lookup k) . readIORef $ rootOccurrence r
739 EventNever -> return Nothing
1160 EventNever -> return Nothing
740 EventPush p -> do
1161 EventPush p -> do
741 subscribed <- getPushSubscribed p
1162 subscribed <- getPushSubscribed p
742− liftIO $ do
1163+ liftIO $ readPushSubscribed subscribed
743− result <- readIORef $ pushSubscribedOccurrence subscribed -- Since ResultM is always called after the final height is reached, this will always be valid
744− touch $ pushSubscribedSelf subscribed
745− return result
746 EventMerge m -> do
1164 EventMerge m -> do
747 subscribed <- getMergeSubscribed m
1165 subscribed <- getMergeSubscribed m
748− liftIO $ do
1166+ liftIO $ readMergeSubscribed subscribed
749− result <- readIORef $ mergeSubscribedOccurrence subscribed
750− touch $ mergeSubscribedSelf subscribed
751− return result
752 EventFan k f -> do
1167 EventFan k f -> do
753− parentOcc <- readEvent $ fanParent f
1168+ subscribed <- getFanSubscribed f
754− return . coerce $ DMap.lookup k =<< parentOcc
1169+ liftIO $ readFanSubscribed k subscribed
755 EventSwitch s -> do
1170 EventSwitch s -> do
756 subscribed <- getSwitchSubscribed s
1171 subscribed <- getSwitchSubscribed s
757− liftIO $ do
1172+ liftIO $ readSwitchSubscribed subscribed
758− result <- readIORef $ switchSubscribedOccurrence subscribed
759− touch $ switchSubscribedSelf subscribed
760− touch $ switchSubscribedOwnInvalidator subscribed
761− return result
762 EventCoincidence c -> do
1173 EventCoincidence c -> do
763 subscribed <- getCoincidenceSubscribed c
1174 subscribed <- getCoincidenceSubscribed c
764− liftIO $ do
1175+ liftIO $ readCoincidenceSubscribed subscribed
1176+ EventHold h -> liftIO . readEventSubscribed =<< getHoldEventSubscription h
1177+ EventDyn d -> liftIO . readEventSubscribed =<< getHoldEventSubscription =<< getDynHold d
1178+ EventHoldIdentity h -> liftIO . readEventSubscribed =<< getHoldEventSubscription h
1179+ EventDynIdentity d -> liftIO . readEventSubscribed =<< getHoldEventSubscription =<< getDynHold d
1180+-}
1181+
1182+readEventSubscribed :: EventSubscribed a -> IO (Maybe a)
1183+readEventSubscribed = \case
1184+ EventSubscribedRoot rs -> readRootSubscribed rs
1185+ EventSubscribedNever -> return Nothing
1186+ EventSubscribedPush ps -> readPushSubscribed ps
1187+ EventSubscribedMerge ms -> readMergeSubscribed ms
1188+ EventSubscribedFan k fs -> readFanSubscribed k fs
1189+ EventSubscribedSwitch ss -> readSwitchSubscribed ss
1190+ EventSubscribedCoincidence cs -> readCoincidenceSubscribed cs
1191+
1192+readRootSubscribed :: RootSubscribed a -> IO (Maybe a)
1193+readRootSubscribed = rootSubscribedOccurrence
1194+
1195+readPushSubscribed :: PushSubscribed b a -> IO (Maybe a)
1196+readPushSubscribed subscribed = do
1197+ result <- readIORef $ pushSubscribedOccurrence subscribed -- Since ResultM is always called after the final height is reached, this will always be valid
1198+ touch $ pushSubscribedSelf subscribed
1199+ return result
1200+
1201+readMergeSubscribed :: MergeSubscribed k -> IO (Maybe (DMap k Identity))
1202+readMergeSubscribed subscribed = do
1203+ result <- readIORef $ mergeSubscribedOccurrence subscribed
1204+ touch $ mergeSubscribedParents subscribed
1205+ return result
1206+
1207+readFanSubscribed :: GCompare k => k a -> FanSubscribed k -> IO (Maybe a)
1208+readFanSubscribed k fs = do
1209+ parentOcc <- readEventSubscribed $ _eventSubscription_subscribed $ fanSubscribedParent fs
1210+ return $ coerce $ DMap.lookup k =<< parentOcc
1211+
1212+readSwitchSubscribed :: SwitchSubscribed a -> IO (Maybe a)
1213+readSwitchSubscribed subscribed = do
1214+ result <- readIORef $ switchSubscribedOccurrence subscribed
1215+ touch $ switchSubscribedOwnInvalidator subscribed
1216+ return result
1217+
1218+readCoincidenceSubscribed :: CoincidenceSubscribed a -> IO (Maybe a)
1219+readCoincidenceSubscribed subscribed = do
765− result <- readIORef $ coincidenceSubscribedOccurrence subscribed
1220+ result <- readIORef $ coincidenceSubscribedOccurrence subscribed
766− touch $ coincidenceSubscribedOuter subscribed
1221+ touch $ coincidenceSubscribedOuter subscribed
767− --TODO: do we need to touch the inner subscriber?
1222+ --TODO: do we need to touch the inner subscriber?
768− return result
1223+ return result
769
1224
770-- Always refers to 0
1225-- Always refers to 0
771{-# NOINLINE zeroRef #-}
1226{-# NOINLINE zeroRef #-}
772−zeroRef :: IORef Int
1227+zeroRef :: IORef Height
773−zeroRef = unsafePerformIO $ newIORef 0
1228+zeroRef = unsafePerformIO $ newIORef zeroHeight
774−
1229+
775−getEventSubscribed :: Event a -> EventM (EventSubscribed a)
1230+{-# SPECIALIZE subscribe :: Event a -> Subscriber a -> EventM x (EventSubscription a) #-}
776−getEventSubscribed e = case e of
1231+subscribe :: forall x m a. CanSubscribe x m => Event a -> Subscriber a -> m (EventSubscription a)
1232+subscribe e sub = case e of
777− EventRoot k r -> liftM EventSubscribedRoot $ getRootSubscribed k r
1233+ EventRoot k r -> wrap EventSubscribedRoot $ liftIO . getRootSubscribed k r
778− EventNever -> return EventSubscribedNever
1234+ EventNever -> return $ EventSubscription Nothing EventSubscribedNever
779− EventPush p -> liftM EventSubscribedPush $ getPushSubscribed p
1235+ EventPush p -> wrap EventSubscribedPush $ getPushSubscribed p
780− EventFan k f -> liftM (EventSubscribedFan k) $ getFanSubscribed f
1236+ EventFan k f -> wrap (EventSubscribedFan k) $ getFanSubscribed k f
781− EventMerge m -> liftM EventSubscribedMerge $ getMergeSubscribed m
1237+ EventMerge m -> wrap EventSubscribedMerge $ getMergeSubscribed m
782− EventSwitch s -> liftM EventSubscribedSwitch $ getSwitchSubscribed s
1238+ EventSwitch s -> wrap EventSubscribedSwitch $ getSwitchSubscribed s
783− EventCoincidence c -> liftM EventSubscribedCoincidence $ getCoincidenceSubscribed c
1239+ EventCoincidence c -> wrap EventSubscribedCoincidence $ getCoincidenceSubscribed c
784−
1240+ EventHold h -> subscribeHoldEvent h sub
1241+ EventDyn j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
1242+ EventHoldIdentity h -> subscribeHoldEvent h sub
1243+ EventDynIdentity j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
1244+ where
1245+ wrap :: (t -> EventSubscribed a) -> (Subscriber a -> m (SubscriberListTicket a, t)) -> m (EventSubscription a)
1246+ wrap tag getSpecificSubscribed = do
1247+ (sln, subd) <- getSpecificSubscribed sub
1248+ return $ EventSubscription (Just sln) $ tag subd
1249+
1250+{-# INLINE subscribeHoldEvent #-}
1251+subscribeHoldEvent :: (CanSubscribe x m, R.Patch p) => Hold p a -> Subscriber (p a) -> m (EventSubscription (p a))
1252+subscribeHoldEvent h sub = do
1253+ EventSubscription _ subd <- getHoldEventSubscription h
1254+ sln <- liftIO $ subscribeEventSubscribed subd sub
1255+ return $ EventSubscription sln subd
1256+
1257+{-# INLINE debugSubscribe #-}
785debugSubscribe :: Bool
1258debugSubscribe :: Bool
786debugSubscribe = False
1259debugSubscribe = False
787
1260
1261+{-# INLINE subscribeEventSubscribed #-}
788−subscribeEventSubscribed :: EventSubscribed a -> WeakSubscriber a -> IO ()
1262+subscribeEventSubscribed :: EventSubscribed a -> Subscriber a -> IO (Maybe (SubscriberListTicket a))
789−subscribeEventSubscribed es ws = case es of
1263+subscribeEventSubscribed es sub = case es of
790 EventSubscribedRoot r -> do
1264 EventSubscribedRoot r -> do
791− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Root"
1265+ traceSubscribe $ "subscribeEventSubscribed Root"
792− modifyIORef' (rootSubscribedSubscribers r) (ws:)
1266+ Just <$> subscribeRootSubscribed r sub
793 EventSubscribedNever -> do
1267 EventSubscribedNever -> do
794− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Never"
1268+ traceSubscribe $ "subscribeEventSubscribed Never"
795− return ()
1269+ return Nothing
796 EventSubscribedPush subscribed -> do
1270 EventSubscribedPush subscribed -> do
797− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Push"
1271+ traceSubscribe $ "subscribeEventSubscribed Push"
798− modifyIORef' (pushSubscribedSubscribers subscribed) (ws:)
1272+ Just <$> subscribePushSubscribed subscribed sub
799 EventSubscribedFan k subscribed -> do
1273 EventSubscribedFan k subscribed -> do
800− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Fan"
1274+ traceSubscribe $ "subscribeEventSubscribed Fan"
801− modifyIORef' (fanSubscribedSubscribers subscribed) $ DMap.insertWith (liftA2 (++)) (FanSubscriberKey k) (Identity [ws])
1275+ Just <$> subscribeFanSubscribed k subscribed sub
802 EventSubscribedMerge subscribed -> do
1276 EventSubscribedMerge subscribed -> do
803− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Merge"
1277+ traceSubscribe $ "subscribeEventSubscribed Merge"
804− modifyIORef' (mergeSubscribedSubscribers subscribed) (ws:)
1278+ Just <$> subscribeMergeSubscribed subscribed sub
805 EventSubscribedSwitch subscribed -> do
1279 EventSubscribedSwitch subscribed -> do
806− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Switch"
1280+ traceSubscribe $ "subscribeEventSubscribed Switch"
807− modifyIORef' (switchSubscribedSubscribers subscribed) (ws:)
1281+ Just <$> subscribeSwitchSubscribed subscribed sub
808 EventSubscribedCoincidence subscribed -> do
1282 EventSubscribedCoincidence subscribed -> do
809− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Coincidence"
1283+ traceSubscribe $ "subscribeEventSubscribed Coincidence"
810− modifyIORef' (coincidenceSubscribedSubscribers subscribed) (ws:)
1284+ Just <$> subscribeCoincidenceSubscribed subscribed sub
811
1285
812−getEventSubscribedOcc :: EventSubscribed a -> IO (Maybe a)
1286+{-# INLINE eventSubscribedHeightRef #-}
813−getEventSubscribedOcc es = case es of
814− EventSubscribedRoot r -> rootSubscribedOccurrence r
815− EventSubscribedNever -> return Nothing
816− EventSubscribedPush subscribed -> readIORef $ pushSubscribedOccurrence subscribed
817− EventSubscribedFan k subscribed -> do
818− parentOcc <- getEventSubscribedOcc $ fanSubscribedParent subscribed
819− let occ = coerce $ DMap.lookup k =<< parentOcc
820− return occ
821− EventSubscribedMerge subscribed -> readIORef $ mergeSubscribedOccurrence subscribed
822− EventSubscribedSwitch subscribed -> readIORef $ switchSubscribedOccurrence subscribed
823− EventSubscribedCoincidence subscribed -> readIORef $ coincidenceSubscribedOccurrence subscribed
824−
825−eventSubscribedHeightRef :: EventSubscribed a -> IORef Int
1287+eventSubscribedHeightRef :: EventSubscribed a -> IORef Height
826−eventSubscribedHeightRef es = case es of
1288+eventSubscribedHeightRef = \case
827 EventSubscribedRoot _ -> zeroRef
1289 EventSubscribedRoot _ -> zeroRef
828 EventSubscribedNever -> zeroRef
1290 EventSubscribedNever -> zeroRef
829 EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
1291 EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
830− EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ fanSubscribedParent subscribed
1292+ EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
831 EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
1293 EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
832 EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
1294 EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
833 EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
1295 EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
834
1296
835−subscribe :: Event a -> WeakSubscriber a -> EventM (EventSubscribed a)
1297+{-# INLINE eventSubscribedHasOwnHeightRef #-}
836−subscribe e ws = do
1298+eventSubscribedHasOwnHeightRef :: EventSubscribed a -> Bool
837− subd <- getEventSubscribed e
1299+eventSubscribedHasOwnHeightRef = \case
838− liftIO $ subscribeEventSubscribed subd ws
1300+ EventSubscribedRoot _ -> False
1301+ EventSubscribedNever -> False
1302+ EventSubscribedPush _ -> False
1303+ EventSubscribedFan _ _ -> False
1304+ EventSubscribedMerge _ -> True
1305+ EventSubscribedSwitch _ -> True
1306+ EventSubscribedCoincidence _ -> True
1307+
1308+{-# INLINE eventSubscribedGetParents #-}
1309+eventSubscribedGetParents :: EventSubscribed a -> IO [Some EventSubscribed]
1310+eventSubscribedGetParents = \case
1311+ EventSubscribedRoot _ -> return []
1312+ EventSubscribedNever -> return []
1313+ EventSubscribedPush subscribed -> return [Some.This $ _eventSubscription_subscribed $ pushSubscribedParent subscribed]
1314+ EventSubscribedFan _ subscribed -> return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
1315+ EventSubscribedMerge subscribed -> do
1316+ ps <- readIORef $ mergeSubscribedParents subscribed
1317+ return $ R.ffor (DMap.toList ps) $ \(_ :=> v) -> Some.This $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription v
1318+ EventSubscribedSwitch subscribed -> do
1319+ s <- readIORef $ switchSubscribedCurrentParent subscribed
1320+ return [Some.This $ _eventSubscription_subscribed s]
1321+ EventSubscribedCoincidence subscribed -> do
1322+ innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
1323+ let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1324+ innerParents = maybeToList $ fmap Some.This innerSubscription
1325+ return $ outerParent : innerParents
1326+
1327+type CanSubscribe x m =
1328+ ( HasSpiderEnv x m
1329+ , HasCurrentHeight m
1330+ , Defer SomeHoldInit m, Defer SomeMergeInit m -- The things we need to lazily initialize state
1331+ , Defer SomeClear m -- Wiping out event firings
1332+ , Defer SomeAssignment m -- Updating state
1333+ , Defer SomeResetCoincidence m
1334+ )
1335+
1336+getRootSubscribed :: GCompare k => k a -> Root k -> Subscriber a -> IO (SubscriberListTicket a, RootSubscribed a)
1337+getRootSubscribed k r sub = do
1338+ mSubscribed <- readIORef $ rootSubscribed r
1339+ case DMap.lookup k mSubscribed of
1340+ Just subscribed -> {-# SCC "hitRoot" #-} do
1341+ sln <- subscribeRootSubscribed subscribed sub
1342+ return (sln, subscribed)
1343+ Nothing -> {-# SCC "missRoot" #-} do
1344+ weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1345+ let !cached = rootSubscribed r
1346+ uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
1347+ (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
1348+ when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1349+ uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1350+ writeIORef uninitRef $! uninit
1351+ let subscribed = RootSubscribed
1352+ { rootSubscribedKey = k
1353+ , rootSubscribedCachedSubscribed = cached
1354+ , rootSubscribedOccurrence = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1355+ , rootSubscribedSubscribers = subs
1356+ , rootSubscribedUninit = uninit
1357+ , rootSubscribedWeakSelf = weakSelf
1358+#ifdef DEBUG_NODEIDS
1359+ , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1360+#endif
1361+ }
1362+ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "RootSubscribed"
1363+ 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
839− return subd
1364+ return (sln, subscribed)
840
1365
841−noinlineFalse :: Bool
1366+{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
842−noinlineFalse = False
1367+cleanupRootSubscribed :: RootSubscribed a -> IO ()
843−{-# NOINLINE noinlineFalse #-}
1368+cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1369+ rootSubscribedUninit self
1370+ modifyIORef' cached $ DMap.delete k
844
1371
845−getRootSubscribed :: GCompare k => k a -> Root k -> EventM (RootSubscribed a)
1372+{-# INLINE subscribeRootSubscribed #-}
846−getRootSubscribed k r = do
1373+subscribeRootSubscribed :: RootSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
847− mSubscribed <- liftIO $ readIORef $ rootSubscribed r
1374+subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
848− case DMap.lookup k mSubscribed of
849− Just subscribed -> return subscribed
850− Nothing -> liftIO $ do
851− subscribersRef <- newIORef []
852− subscribed <- newRootSubscribed (liftM (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r) subscribersRef
853− -- Strangely, init needs the same stuff as a RootSubscribed has, but it must not be the same as the one that everyone's subscribing to, or it'll leak memory
854− uninit <- rootInit r k $ RootTrigger (subscribersRef, rootOccurrence r, k)
855− addFinalizer subscribed $ do
856− when noinlineFalse $ putStr "" -- For some reason, without this line, the finalizer will run earlier than it should
857−-- putStrLn "Uninit root"
858− uninit
859− liftIO $ modifyIORef' (rootSubscribed r) $ DMap.insert k subscribed
860− return subscribed
861
1375
862-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
1376-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
863−getPushSubscribed :: Push a b -> EventM (PushSubscribed a b)
1377+{-# SPECIALIZE getPushSubscribed :: Push a b -> Subscriber b -> EventM x (SubscriberListTicket b, PushSubscribed a b) #-}
1378+getPushSubscribed :: CanSubscribe x m => Push a b -> Subscriber b -> m (SubscriberListTicket b, PushSubscribed a b)
864−getPushSubscribed p = do
1379+getPushSubscribed p sub = do
865 mSubscribed <- liftIO $ readIORef $ pushSubscribed p
1380 mSubscribed <- liftIO $ readIORef $ pushSubscribed p
866 case mSubscribed of
1381 case mSubscribed of
1382+ Just subscribed -> {-# SCC "hitPush" #-} do
1383+ sln <- liftIO $ subscribePushSubscribed subscribed sub
867− Just subscribed -> return subscribed
1384+ return (sln, subscribed)
868− Nothing -> do -- Not yet subscribed
1385+ Nothing -> {-# SCC "missPush" #-} do -- Not yet subscribed
1386+ subscribedRef <- liftIO $ newIORef $ error "getPushSubscribed: subscribed not yet intialized"
869− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ pushSubscribed p
1387+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
870 s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
1388 s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
871− ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberPush"
872− subd <- subscribe (pushParent p) $ WeakSubscriberSimple ws
1389+ subscription@(EventSubscription _ subd) <- subscribe (pushParent p) s
873− parentOcc <- liftIO $ getEventSubscribedOcc subd
1390+ parentOcc <- liftIO $ readEventSubscribed subd
874− occ <- liftM join $ mapM (pushCompute p) parentOcc
1391+ occ <- runComputeM $ join <$> mapM (pushCompute p) parentOcc
875 occRef <- liftIO $ newIORef occ
1392 occRef <- liftIO $ newIORef occ
876 when (isJust occ) $ scheduleClear occRef
1393 when (isJust occ) $ scheduleClear occRef
877− subscribersRef <- liftIO $ newIORef []
1394+ weakSelf <- liftIO $ newIORef $ error "getPushSubscribed: weakSelf not initialized"
1395+ let !cached = pushSubscribed p
1396+ (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupPushSubscribed
878 let subscribed = PushSubscribed
1397 let subscribed = PushSubscribed
1398+ { pushSubscribedCachedSubscribed = cached
879− { pushSubscribedOccurrence = occRef
1399+ , pushSubscribedOccurrence = occRef
880 , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
1400 , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
881− , pushSubscribedSubscribers = subscribersRef
1401+ , pushSubscribedSubscribers = subs
882− , pushSubscribedSelf = unsafeCoerce s
1402+ , pushSubscribedSelf = s
1403+ , pushSubscribedWeakSelf = weakSelf
883− , pushSubscribedParent = subd
1404+ , pushSubscribedParent = subscription
884#ifdef DEBUG_NODEIDS
1405#ifdef DEBUG_NODEIDS
885 , pushSubscribedNodeId = unsafeNodeId p
1406 , pushSubscribedNodeId = unsafeNodeId p
886#endif
1407#endif
887 }
1408 }
1409+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
1410+ liftIO $ writeIORef subscribedRef $! subscribed
888 liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
1411 liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
889− return subscribed
1412+ return (sln, subscribed)
1413+
1414+cleanupPushSubscribed :: PushSubscribed a b -> IO ()
1415+cleanupPushSubscribed self = do
1416+ unsubscribe $ pushSubscribedParent self
1417+ writeIORef (pushSubscribedCachedSubscribed self) $ Nothing
1418+
1419+{-# INLINE subscribePushSubscribed #-}
1420+subscribePushSubscribed :: PushSubscribed a b -> Subscriber b -> IO (SubscriberListTicket b)
1421+subscribePushSubscribed subscribed sub = WeakBag.insert sub (pushSubscribedSubscribers subscribed) (pushSubscribedWeakSelf subscribed) cleanupPushSubscribed
890
1422
1423+{-# SPECIALIZE getMergeSubscribed :: GCompare k => Merge k -> Subscriber (DMap k Identity) -> EventM x (SubscriberListTicket (DMap k Identity), MergeSubscribed k) #-}
891−getMergeSubscribed :: forall k. GCompare k => Merge k -> EventM (MergeSubscribed k)
1424+getMergeSubscribed :: forall x k m. (CanSubscribe x m, GCompare k) => Merge k -> Subscriber (DMap k Identity) -> m (SubscriberListTicket (DMap k Identity), MergeSubscribed k)
892−getMergeSubscribed m = {-# SCC "getMergeSubscribed.entire" #-} do
1425+getMergeSubscribed m sub = do
893 mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
1426 mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
894 case mSubscribed of
1427 case mSubscribed of
1428+ Just subscribed -> {-# SCC "hitMerge" #-} do
1429+ sln <- liftIO $ subscribeMergeSubscribed subscribed sub
895− Just subscribed -> return subscribed
1430+ return (sln, subscribed)
896− Nothing -> if DMap.null $ mergeParents m then emptyMergeSubscribed else do
1431+ Nothing -> {-# SCC "missMerge" #-} do
897 subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
1432 subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
898 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1433 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
899− s <- liftIO $ newBox subscribedUnsafe
1434+ initialParents <- readBehaviorUntracked $ current $ mergeParents m
900− ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberMerge"
901− subscribers :: [(Any, Maybe (DSum k Identity), Int, DSum k EventSubscribed)] <- forM (DMap.toList $ mergeParents m) $ {-# SCC "getMergeSubscribed.a" #-} \(k :=> e) -> {-# SCC "getMergeSubscribed.a1" #-} do
1435+ subscribers :: [(Maybe (DSum k Identity), Height, DSum k (MergeSubscribedParent k))] <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
902− parentSubd <- {-# SCC "getMergeSubscribed.a.parentSubd" #-} subscribe e $ WeakSubscriberMerge k ws
1436+ s <- liftIO $ newSubscriberMerge k subscribedUnsafe
1437+ subscription@(EventSubscription _ parentSubd) <- subscribe e s
903− parentOcc <- {-# SCC "getMergeSubscribed.a.parentOcc" #-} liftIO $ getEventSubscribedOcc parentSubd
1438+ parentOcc <- liftIO $ readEventSubscribed parentSubd
904− height <- {-# SCC "getMergeSubscribed.a.height" #-} liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
1439+ height <- liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
905− return $ {-# SCC "getMergeSubscribed.a.returnVal" #-} (unsafeCoerce s :: Any, fmap (\x -> k :=> Identity x) parentOcc, height, k :=> parentSubd)
1440+ return $ (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription s)
906− let dm = DMap.fromDistinctAscList $ catMaybes $ map (\(_, x, _, _) -> x) subscribers
1441+ let dm = DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
907− subscriberHeights = map (\(_, _, x, _) -> x) subscribers
1442+ heights = fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
908− myHeight =
1443+ myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
909− if any (==invalidHeight) subscriberHeights --TODO: Replace 'any' with invalidHeight-preserving 'maximum'
1444+ myHeight = if invalidHeight `elem` heights
910− then invalidHeight
1445+ then invalidHeight
911− else succ $ Prelude.maximum subscriberHeights -- This is safe because the DMap will never be empty here
1446+ else succHeight $ heightBagMax myHeightBag
912 currentHeight <- getCurrentHeight
1447 currentHeight <- getCurrentHeight
913 let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1448 let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
914 then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1449 then (if DMap.null dm then Nothing else Just dm, DMap.empty)
915 else (Nothing, dm)
1450 else (Nothing, dm)
916− when (not $ DMap.null accum) $ do
1451+ unless (DMap.null accum) $ do
917 scheduleMerge myHeight subscribedUnsafe
1452 scheduleMerge myHeight subscribedUnsafe
918 occRef <- liftIO $ newIORef occ
1453 occRef <- liftIO $ newIORef occ
919 when (isJust occ) $ scheduleClear occRef
1454 when (isJust occ) $ scheduleClear occRef
920 accumRef <- liftIO $ newIORef accum
1455 accumRef <- liftIO $ newIORef accum
921 heightRef <- liftIO $ newIORef myHeight
1456 heightRef <- liftIO $ newIORef myHeight
922− subsRef <- liftIO $ newIORef []
1457+ heightMapRef <- liftIO $ newIORef myHeightBag
1458+ weakSelf <- liftIO $ newIORef $ error "getMergeSubscribed: weakSelf not yet initialized"
1459+ (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupMergeSubscribed
1460+ changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1461+ parentsRef <- liftIO $ newIORef $ DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
923− let subscribed = MergeSubscribed
1462+ let !subscribed = MergeSubscribed
1463+ { mergeSubscribedCachedSubscribed = mergeSubscribed m
924− { mergeSubscribedOccurrence = occRef
1464+ , mergeSubscribedOccurrence = occRef
925 , mergeSubscribedAccum = accumRef
1465 , mergeSubscribedAccum = accumRef
926 , mergeSubscribedHeight = heightRef
1466 , mergeSubscribedHeight = heightRef
1467+ , mergeSubscribedHeightBag = heightMapRef
927− , mergeSubscribedSubscribers = subsRef
1468+ , mergeSubscribedSubscribers = subs
928− , mergeSubscribedSelf = unsafeCoerce $ map (\(x, _, _, _) -> x) subscribers --TODO: Does lack of strictness make this leak?
1469+ , mergeSubscribedChange = changeSubdRef
929− , mergeSubscribedParents = DMap.fromDistinctAscList $ map (\(_, _, _, x) -> x) subscribers
1470+ , mergeSubscribedParents = parentsRef
1471+ , mergeSubscribedWeakSelf = weakSelf
930#ifdef DEBUG_NODEIDS
1472#ifdef DEBUG_NODEIDS
931 , mergeSubscribedNodeId = unsafeNodeId m
1473 , mergeSubscribedNodeId = unsafeNodeId m
932#endif
1474#endif
933 }
1475 }
1476+ defer $ SomeMergeInit subscribed $ updated $ mergeParents m
934− liftIO $ writeIORef subscribedRef subscribed
1477+ liftIO $ writeIORef subscribedRef $! subscribed
1478+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "MergeSubscribed"
1479+ liftIO $ writeIORef (mergeSubscribed m) $ Just subscribed
935− return subscribed
1480+ return (sln, subscribed)
936− where emptyMergeSubscribed = do --TODO: This should never happen
1481+
937− occRef <- liftIO $ newIORef Nothing
1482+cleanupMergeSubscribed :: MergeSubscribed k -> IO ()
938− accumRef <- liftIO $ newIORef DMap.empty
1483+cleanupMergeSubscribed subscribed = do
939− subsRef <- liftIO $ newIORef []
1484+ parents <- readIORef $ mergeSubscribedParents subscribed
940− return $ MergeSubscribed
1485+ forM_ (DMap.toList parents) $ \(_ :=> msp) -> unsubscribe $ mergeSubscribedParentSubscription msp
941− { mergeSubscribedOccurrence = occRef
1486+ -- Not necessary, because this whole MergeSubscribed is dead: writeIORef (mergeSubscribedParents subscribed) DMap.empty
942− , mergeSubscribedAccum = accumRef
1487+ writeIORef (mergeSubscribedCachedSubscribed subscribed) $ Nothing -- Get rid of the cached subscribed
943− , mergeSubscribedHeight = zeroRef
1488+
944− , mergeSubscribedSubscribers = subsRef --TODO: This will definitely leak
1489+{-# INLINE subscribeMergeSubscribed #-}
945− , mergeSubscribedSelf = unsafeCoerce ()
1490+subscribeMergeSubscribed :: MergeSubscribed k -> Subscriber (DMap k Identity) -> IO (SubscriberListTicket (DMap k Identity))
946− , mergeSubscribedParents = DMap.empty
1491+subscribeMergeSubscribed subscribed sub = WeakBag.insert sub (mergeSubscribedSubscribers subscribed) (mergeSubscribedWeakSelf subscribed) cleanupMergeSubscribed
947−#ifdef DEBUG_NODEIDS
1492+
948− , mergeSubscribedNodeId = -1
949−#endif
950− }
951−
952−getFanSubscribed :: GCompare k => Fan k -> EventM (FanSubscribed k)
1493+{-# SPECIALIZE getFanSubscribed :: GCompare k => k a -> Fan k -> Subscriber a -> EventM x (SubscriberListTicket a, FanSubscribed k) #-}
953−getFanSubscribed f = do
1494+getFanSubscribed :: (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderEnv x m, GCompare k) => k a -> Fan k -> Subscriber a -> m (SubscriberListTicket a, FanSubscribed k)
1495+getFanSubscribed k f sub = do
954 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1496 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
955 case mSubscribed of
1497 case mSubscribed of
1498+ Just subscribed -> {-# SCC "hitFan" #-} do
1499+ sln <- liftIO $ subscribeFanSubscribed k subscribed sub
956− Just subscribed -> return subscribed
1500+ return (sln, subscribed)
957− Nothing -> do
1501+ Nothing -> {-# SCC "missFan" #-} do
1502+ subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
958− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ fanSubscribed f
1503+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
959− sub <- liftIO $ newSubscriberFan subscribedUnsafe
1504+ s <- liftIO $ newSubscriberFan subscribedUnsafe
960− wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberFan"
961− subd <- subscribe (fanParent f) $ WeakSubscriberSimple wsub
1505+ subscription <- subscribe (fanParent f) s
962− subscribersRef <- liftIO $ newIORef DMap.empty
1506+ weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1507+ (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1508+ subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
963 let subscribed = FanSubscribed
1509 let subscribed = FanSubscribed
1510+ { fanSubscribedCachedSubscribed = fanSubscribed f
964− { fanSubscribedParent = subd
1511+ , fanSubscribedParent = subscription
965 , fanSubscribedSubscribers = subscribersRef
1512 , fanSubscribedSubscribers = subscribersRef
966− , fanSubscribedSelf = sub
967#ifdef DEBUG_NODEIDS
1513#ifdef DEBUG_NODEIDS
968 , fanSubscribedNodeId = unsafeNodeId f
1514 , fanSubscribedNodeId = unsafeNodeId f
969#endif
1515#endif
970 }
1516 }
1517+ let !self = (k, subscribed)
1518+ liftIO $ writeIORef subscribersRef $! DMap.singleton k $ FanSubscribedChildren subsForK self weakSelf
1519+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1520+ liftIO $ writeIORef subscribedRef $! subscribed
971 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1521 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
972− return subscribed
1522+ return (slnForSub, subscribed)
973−
1523+
1524+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed k) -> IO ()
1525+cleanupFanSubscribed (k, subscribed) = do
1526+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1527+ let reducedSubscribers = DMap.delete k subscribers
1528+ if DMap.null reducedSubscribers
1529+ then do
1530+ unsubscribe $ fanSubscribedParent subscribed
1531+ -- Not necessary in this case, because this whole FanSubscribed is dead: writeIORef (fanSubscribedSubscribers subscribed) reducedSubscribers
1532+ writeIORef (fanSubscribedCachedSubscribed subscribed) Nothing
1533+ else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1534+
1535+{-# INLINE subscribeFanSubscribed #-}
1536+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed k -> Subscriber a -> IO (SubscriberListTicket a)
1537+subscribeFanSubscribed k subscribed sub = do
1538+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1539+ case DMap.lookup k subscribers of
1540+ Nothing -> {-# SCC "missSubscribeFanSubscribed" #-} do
1541+ let !self = (k, subscribed)
1542+ weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1543+ (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1544+ 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
1545+ return sln
1546+ Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1547+
974−getSwitchSubscribed :: Switch a -> EventM (SwitchSubscribed a)
1548+{-# SPECIALIZE getSwitchSubscribed :: Switch a -> Subscriber a -> EventM x (SubscriberListTicket a, SwitchSubscribed a) #-}
1549+getSwitchSubscribed :: (Defer SomeHoldInit m, Defer SomeAssignment m, Defer SomeMergeInit m, Defer SomeClear m, Defer SomeResetCoincidence m, HasCurrentHeight m, HasSpiderEnv x m) => Switch a -> Subscriber a -> m (SubscriberListTicket a, SwitchSubscribed a)
975−getSwitchSubscribed s = do
1550+getSwitchSubscribed s sub = do
976 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1551 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
977 case mSubscribed of
1552 case mSubscribed of
1553+ Just subscribed -> {-# SCC "hitSwitch" #-} do
1554+ sln <- liftIO $ subscribeSwitchSubscribed subscribed sub
978− Just subscribed -> return subscribed
1555+ return (sln, subscribed)
979− Nothing -> do
1556+ Nothing -> {-# SCC "missSwitch" #-} do
980 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1557 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
981 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1558 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
982 i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1559 i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
983− sub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1560+ mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
984 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1561 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
985 wiRef <- liftIO $ newIORef wi
1562 wiRef <- liftIO $ newIORef wi
986− wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberSwitch"
987− selfWeakRef <- liftIO $ newIORef wsub
988 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1563 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1564+ holdInits <- getDeferralQueue
989− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) $ Just (wi, parentsRef)
1565+ e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
990− subd <- subscribe e $ WeakSubscriberSimple wsub
1566+ subscription@(EventSubscription _ subd) <- subscribe e mySub
991− subdRef <- liftIO $ newIORef subd
1567+ subscriptionRef <- liftIO $ newIORef subscription
992− parentOcc <- liftIO $ getEventSubscribedOcc subd
1568+ parentOcc <- liftIO $ readEventSubscribed subd
993 occRef <- liftIO $ newIORef parentOcc
1569 occRef <- liftIO $ newIORef parentOcc
994 when (isJust parentOcc) $ scheduleClear occRef
1570 when (isJust parentOcc) $ scheduleClear occRef
995 heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
1571 heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
996− subscribersRef <- liftIO $ newIORef []
1572+ weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1573+ (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
997− let subscribed = SwitchSubscribed
1574+ let !subscribed = SwitchSubscribed
1575+ { switchSubscribedCachedSubscribed = switchSubscribed s
998− { switchSubscribedOccurrence = occRef
1576+ , switchSubscribedOccurrence = occRef
999 , switchSubscribedHeight = heightRef
1577 , switchSubscribedHeight = heightRef
1000− , switchSubscribedSubscribers = subscribersRef
1578+ , switchSubscribedSubscribers = subs
1001− , switchSubscribedSelf = sub
1002− , switchSubscribedSelfWeak = selfWeakRef
1003 , switchSubscribedOwnInvalidator = i
1579 , switchSubscribedOwnInvalidator = i
1004 , switchSubscribedOwnWeakInvalidator = wiRef
1580 , switchSubscribedOwnWeakInvalidator = wiRef
1005 , switchSubscribedBehaviorParents = parentsRef
1581 , switchSubscribedBehaviorParents = parentsRef
1006 , switchSubscribedParent = switchParent s
1582 , switchSubscribedParent = switchParent s
1007− , switchSubscribedCurrentParent = subdRef
1583+ , switchSubscribedCurrentParent = subscriptionRef
1584+ , switchSubscribedWeakSelf = weakSelf
1008#ifdef DEBUG_NODEIDS
1585#ifdef DEBUG_NODEIDS
1009 , switchSubscribedNodeId = unsafeNodeId s
1586 , switchSubscribedNodeId = unsafeNodeId s
1010#endif
1587#endif
1011 }
1588 }
1589+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1012− liftIO $ writeIORef subscribedRef subscribed
1590+ liftIO $ writeIORef subscribedRef $! subscribed
1013 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1591 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1014− return subscribed
1592+ return (slnForSub, subscribed)
1015
1593
1016−getCoincidenceSubscribed :: forall a. Coincidence a -> EventM (CoincidenceSubscribed a)
1594+cleanupSwitchSubscribed :: SwitchSubscribed a -> IO ()
1595+cleanupSwitchSubscribed subscribed = do
1596+ unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1597+ finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1598+ writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1599+
1600+{-# INLINE subscribeSwitchSubscribed #-}
1601+subscribeSwitchSubscribed :: SwitchSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1602+subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1603+
1604+{-# SPECIALIZE getCoincidenceSubscribed :: Coincidence a -> Subscriber a -> EventM x (SubscriberListTicket a, CoincidenceSubscribed a) #-}
1605+getCoincidenceSubscribed :: forall x a m. (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderEnv x m) => Coincidence a -> Subscriber a -> m (SubscriberListTicket a, CoincidenceSubscribed a)
1017−getCoincidenceSubscribed c = do
1606+getCoincidenceSubscribed c sub = do
1018 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1607 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1019 case mSubscribed of
1608 case mSubscribed of
1609+ Just subscribed -> {-# SCC "hitCoincidence" #-} do
1610+ sln <- liftIO $ subscribeCoincidenceSubscribed subscribed sub
1020− Just subscribed -> return subscribed
1611+ return (sln, subscribed)
1021− Nothing -> do
1612+ Nothing -> {-# SCC "missCoincidence" #-} do
1022 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1613 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1023 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1614 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1024 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1615 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1025− wsubOuter <- liftIO $ mkWeakPtrWithDebug subOuter "subOuter"
1026− outerSubd <- subscribe (coincidenceParent c) $ WeakSubscriberSimple wsubOuter
1616+ outerSubscription@(EventSubscription _ outerSubd) <- subscribe (coincidenceParent c) subOuter
1027− outerOcc <- liftIO $ getEventSubscribedOcc outerSubd
1617+ outerOcc <- liftIO $ readEventSubscribed outerSubd
1028 outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
1618 outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
1029 (occ, height, mInnerSubd) <- case outerOcc of
1619 (occ, height, mInnerSubd) <- case outerOcc of
1030 Nothing -> return (Nothing, outerHeight, Nothing)
1620 Nothing -> return (Nothing, outerHeight, Nothing)
@@ -1036,23 +1626,37 @@ getCoincidenceSubscribed c = do
1036 heightRef <- liftIO $ newIORef height
1626 heightRef <- liftIO $ newIORef height
1037 innerSubdRef <- liftIO $ newIORef mInnerSubd
1627 innerSubdRef <- liftIO $ newIORef mInnerSubd
1038 scheduleClear innerSubdRef
1628 scheduleClear innerSubdRef
1039− subscribersRef <- liftIO $ newIORef []
1629+ weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1630+ (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1040 let subscribed = CoincidenceSubscribed
1631 let subscribed = CoincidenceSubscribed
1632+ { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1041− { coincidenceSubscribedOccurrence = occRef
1633+ , coincidenceSubscribedOccurrence = occRef
1042 , coincidenceSubscribedHeight = heightRef
1634 , coincidenceSubscribedHeight = heightRef
1043− , coincidenceSubscribedSubscribers = subscribersRef
1635+ , coincidenceSubscribedSubscribers = subs
1044 , coincidenceSubscribedOuter = subOuter
1636 , coincidenceSubscribedOuter = subOuter
1045− , coincidenceSubscribedOuterParent = outerSubd
1637+ , coincidenceSubscribedOuterParent = outerSubscription
1046 , coincidenceSubscribedInnerParent = innerSubdRef
1638 , coincidenceSubscribedInnerParent = innerSubdRef
1639+ , coincidenceSubscribedWeakSelf = weakSelf
1047#ifdef DEBUG_NODEIDS
1640#ifdef DEBUG_NODEIDS
1048 , coincidenceSubscribedNodeId = unsafeNodeId c
1641 , coincidenceSubscribedNodeId = unsafeNodeId c
1049#endif
1642#endif
1050 }
1643 }
1644+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1051− liftIO $ writeIORef subscribedRef subscribed
1645+ liftIO $ writeIORef subscribedRef $! subscribed
1052 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1646 liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1053− return subscribed
1647+ return (slnForSub, subscribed)
1648+
1649+cleanupCoincidenceSubscribed :: CoincidenceSubscribed a -> IO ()
1650+cleanupCoincidenceSubscribed subscribed = do
1651+ unsubscribe $ coincidenceSubscribedOuterParent subscribed
1652+ writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1653+
1654+{-# INLINE subscribeCoincidenceSubscribed #-}
1655+subscribeCoincidenceSubscribed :: CoincidenceSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1656+subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1054
1657
1658+{-# INLINE merge #-}
1055−merge :: GCompare k => DMap k Event -> Event (DMap k Identity)
1659+merge :: GCompare k => Dynamic R.PatchDMap (DMap k Event) -> Event (DMap k Identity)
1056merge m = EventMerge $ Merge
1660merge m = EventMerge $ Merge
1057 { mergeParents = m
1661 { mergeParents = m
1058 , mergeSubscribed = unsafeNewIORef m Nothing
1662 , mergeSubscribed = unsafeNewIORef m Nothing
@@ -1068,218 +1672,297 @@ fan e =
1068 }
1672 }
1069 in EventSelector $ \k -> EventFan k f
1673 in EventSelector $ \k -> EventFan k f
1070
1674
1675+runHoldInits :: IORef [SomeHoldInit] -> IORef [SomeMergeInit] -> EventM x ()
1676+runHoldInits holdInitRef mergeInitRef = do
1677+ holdInits <- liftIO $ readIORef holdInitRef
1678+ mergeInits <- liftIO $ readIORef mergeInitRef
1679+ unless (null holdInits && null mergeInits) $ do
1680+ liftIO $ writeIORef holdInitRef []
1681+ liftIO $ writeIORef mergeInitRef []
1682+ mapM_ initHold holdInits
1683+ mapM_ initMerge mergeInits
1684+ runHoldInits holdInitRef mergeInitRef
1685+
1686+initHold :: SomeHoldInit -> EventM x ()
1687+initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1688+
1689+initMerge :: SomeMergeInit -> EventM x ()
1690+initMerge (SomeMergeInit subscribed changed) = do
1691+ changeSub <- liftIO $ newSubscriberMergeChange subscribed
1692+ EventSubscription _ changeSubd <- subscribe changed changeSub
1693+ change <- liftIO $ readEventSubscribed changeSubd
1694+ forM_ change $ \c -> defer $ SomeMergeUpdate c subscribed
1695+ liftIO $ writeIORef (mergeSubscribedChange subscribed) (changeSub, changeSubd)
1696+
1071-- | Run an event action outside of a frame
1697-- | Run an event action outside of a frame
1072−runFrame :: EventM a -> IO a
1698+runFrame :: EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1073−runFrame a = do
1699+runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1700+ -- 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
1701+ pendingCleanups <- atomicModifyIORef (_spiderEnv_cleanups spiderEnv) $ \x -> ([], x)
1702+ {-# SCC "cleanups" #-} sequence_ pendingCleanups
1074 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1703 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1075 holdInitRef <- newIORef []
1704 holdInitRef <- newIORef []
1705+ mergeUpdateRef <- newIORef []
1706+ mergeInitRef <- newIORef []
1076− heightRef <- newIORef 0
1707+ heightRef <- newIORef zeroHeight
1077 toClearRef <- newIORef []
1708 toClearRef <- newIORef []
1078 toClearRootRef <- newIORef []
1709 toClearRootRef <- newIORef []
1079 coincidenceInfosRef <- newIORef []
1710 coincidenceInfosRef <- newIORef []
1080 delayedRef <- liftIO $ newIORef IntMap.empty
1711 delayedRef <- liftIO $ newIORef IntMap.empty
1081− result <- flip runEventM (EventEnv toAssignRef holdInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) $ do
1712+ let env = EventEnv spiderEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1082− result <- a
1083− let runHoldInits = do
1713+ let go = do
1084− holdInits <- liftIO $ readIORef holdInitRef
1714+ result <- a
1085− if null holdInits then return () else do
1086− liftIO $ writeIORef holdInitRef []
1087− forM_ holdInits $ \(SomeHoldInit e h) -> subscribeHold e h
1088− runHoldInits
1089− runHoldInits -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
1715+ 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
1090− return result
1716+ return result
1717+ result <- runEventM go env
1091 toClear <- readIORef toClearRef
1718 toClear <- readIORef toClearRef
1092− forM_ toClear $ \(SomeMaybeIORef ref) -> writeIORef ref Nothing
1719+ forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1093 toClearRoot <- readIORef toClearRootRef
1720 toClearRoot <- readIORef toClearRootRef
1094− forM_ toClearRoot $ \(SomeDMapIORef ref) -> writeIORef ref DMap.empty
1721+ forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1095 toAssign <- readIORef toAssignRef
1722 toAssign <- readIORef toAssignRef
1096 toReconnectRef <- newIORef []
1723 toReconnectRef <- newIORef []
1097− forM_ toAssign $ \(SomeAssignment h v) -> do
1098− writeIORef (holdValue h) v
1099− writeIORef (holdInvalidators h) =<< invalidate toReconnectRef =<< readIORef (holdInvalidators h)
1100 coincidenceInfos <- readIORef coincidenceInfosRef
1724 coincidenceInfos <- readIORef coincidenceInfosRef
1101− forM_ coincidenceInfos $ \(SomeCoincidenceInfo wsubInner subInner mcs) -> do
1725+ forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1102− touch subInner
1726+ writeIORef vRef v
1103− finalize wsubInner
1727+ when debugInvalidate $ putStrLn $ "Invalidating Hold"
1104− mapM_ invalidateCoincidenceHeight mcs
1728+ writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1729+ mergeUpdates <- readIORef mergeUpdateRef
1730+ writeIORef mergeUpdateRef []
1731+ let updateMerge :: SomeMergeUpdate -> IO [SomeEventSubscription]
1732+ updateMerge (SomeMergeUpdate (R.PatchDMap p :: R.PatchDMap (DMap k Event)) m) = do
1733+ oldParents <- readIORef $ mergeSubscribedParents m
1734+ let f (subscriptionsToKill, ps) (k :=> Compose me) = do
1735+ (mOldSubd, newPs) <- case me of
1736+ Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1737+ Just e -> do
1738+ s <- liftIO $ newSubscriberMerge k m
1739+ subscription@(EventSubscription _ subd) <- subscribe e s
1740+ let newParent = MergeSubscribedParent subscription s
1741+ newParentHeight <- liftIO $ getEventSubscribedHeight subd
1742+ liftIO $ addMergeHeight newParentHeight m
1743+ return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1744+ forM_ mOldSubd $ \oldSubd -> do
1745+ oldHeight <- liftIO $ readIORef $ eventSubscribedHeightRef $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription oldSubd
1746+ liftIO $ removeMergeHeight oldHeight m
1747+ return (maybeToList (SomeEventSubscription . mergeSubscribedParentSubscription <$> mOldSubd) ++ subscriptionsToKill, newPs)
1748+ (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
1749+ writeIORef (mergeSubscribedParents m) $! newParents
1750+ return subscriptionsToKill
1751+ when debugPropagate $ putStrLn "Updating merges"
1752+ mergeSubscriptionsToKill <- concat <$> mapM updateMerge mergeUpdates
1753+ when debugPropagate $ putStrLn "Updating merges done"
1105 toReconnect <- readIORef toReconnectRef
1754 toReconnect <- readIORef toReconnectRef
1106− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1755+ switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1107− wsub <- readIORef $ switchSubscribedSelfWeak subscribed
1756+ oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1108− finalize wsub
1109 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1757 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1758+ when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
1110 finalize wi
1759 finalize wi
1111 i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1760 i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1112 wi' <- mkWeakPtrWithDebug i "wi'"
1761 wi' <- mkWeakPtrWithDebug i "wi'"
1762+ writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1113 writeIORef (switchSubscribedBehaviorParents subscribed) []
1763 writeIORef (switchSubscribedBehaviorParents subscribed) []
1764+ writeIORef holdInitRef [] --TODO: Should we reuse this?
1114− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed))
1765+ e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) holdInitRef
1766+ 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
1115 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1767 --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1116− sub <- evaluate $ switchSubscribedSelf subscribed -- Must be done strictly, or the weak pointer will refer to a useless thunk
1768+ sub <- newSubscriberSwitch subscribed
1117− wsub' <- mkWeakPtrWithDebug sub "wsub'"
1118− writeIORef (switchSubscribedSelfWeak subscribed) wsub'
1119− subd' <- runFrame $ subscribe e $ WeakSubscriberSimple wsub' --TODO: Assert that the event isn't firing --TODO: This should not loop because none of the events should be firing, but still, it is inefficient
1769+ 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
1120 {-
1770 {-
1121− stackTrace <- liftIO $ liftM renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1771+ stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1122 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1772 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1123 EventRoot _ -> "EventRoot"
1773 EventRoot _ -> "EventRoot"
1124 EventNever -> "EventNever"
1774 EventNever -> "EventNever"
1125 _ -> "something else"
1775 _ -> "something else"
1126 -}
1776 -}
1127− writeIORef (switchSubscribedCurrentParent subscribed) subd'
1777+ writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1778+ return $ SomeEventSubscription oldSubscription
1779+ forM_ mergeSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1780+ forM_ switchSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1781+ forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1782+ EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1128 parentHeight <- readIORef $ eventSubscribedHeightRef subd'
1783 parentHeight <- readIORef $ eventSubscribedHeightRef subd'
1129 myHeight <- readIORef $ switchSubscribedHeight subscribed
1784 myHeight <- readIORef $ switchSubscribedHeight subscribed
1130− if parentHeight == myHeight then return () else do
1785+ when (parentHeight /= myHeight) $ do
1131− writeIORef (switchSubscribedHeight subscribed) parentHeight
1786+ writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1132− mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1787+ WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1788+ forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> do
1789+ 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
1790+ forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1791+ unsubscribe subscription
1792+ mapM_ invalidateCoincidenceHeight mcs
1133− forM_ coincidenceInfos $ \(SomeCoincidenceInfo _ _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1793+ forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1794+ forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> revalidateMergeHeight m
1134 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1795 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1135− mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1796+ height <- calculateSwitchHeight subscribed
1797+ updateSwitchHeight height subscribed
1136 return result
1798 return result
1137
1799
1138−invalidHeight :: Int
1800+newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1139−invalidHeight = -1000
1801+
1140−
1802+{-# INLINE zeroHeight #-}
1141−invalidateSubscriberHeight :: WeakSubscriber a -> IO ()
1803+zeroHeight :: Height
1142−invalidateSubscriberHeight ws = do
1804+zeroHeight = Height 0
1143− ms <- deRefWeakSubscriber ws
1805+
1144− case ms of
1806+{-# INLINE invalidHeight #-}
1145− Nothing -> return () --TODO: cleanup?
1807+invalidHeight :: Height
1146− Just s -> case s of
1808+invalidHeight = Height (-1000)
1147− SubscriberPush _ subscribed -> do
1809+
1148− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1810+-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1149− mapM_ invalidateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1811+{-# INLINE invalidHeightBeingTraversed #-}
1150− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1812+invalidHeightBeingTraversed :: Height
1151− SubscriberMerge _ subscribed -> do
1813+invalidHeightBeingTraversed = Height (-1001)
1152− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1814+
1153− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1815+{-# INLINE succHeight #-}
1154− when (oldHeight /= invalidHeight) $ do
1816+succHeight :: Height -> Height
1155− writeIORef (mergeSubscribedHeight subscribed) $ invalidHeight
1817+succHeight h@(Height a) =
1156− mapM_ invalidateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1818+ if h == invalidHeight
1157− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1819+ then invalidHeight
1158− SubscriberFan subscribed -> do
1820+ else Height $ succ a
1159− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1821+
1160− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1822+invalidateSubscriberHeight :: Height -> Subscriber a -> IO ()
1161− forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ invalidateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
1823+invalidateSubscriberHeight old s = case s of
1162− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1824+ SubscriberPush _ subscribed -> do
1163− SubscriberHold _ -> return ()
1825+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1164− SubscriberSwitch subscribed -> do
1826+ WeakBag.traverse (pushSubscribedSubscribers subscribed) $ invalidateSubscriberHeight old
1165− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1827+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1166− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1828+ SubscriberMerge _ subscribed -> do
1167− when (oldHeight /= invalidHeight) $ do
1829+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1168− writeIORef (switchSubscribedHeight subscribed) $ invalidHeight
1830+ removeMergeHeight old subscribed
1169− mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1831+ invalidateMergeHeight subscribed
1170− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1832+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1171− SubscriberCoincidenceOuter subscribed -> do
1833+ SubscriberMergeChange _ -> return () -- Doesn't affect the height of the merge, because its changes are delayed
1172− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1834+ SubscriberFan subscribed -> do
1173− invalidateCoincidenceHeight subscribed
1835+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1174− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1836+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1175− SubscriberCoincidenceInner subscribed -> do
1837+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
1176− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1838+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1177− invalidateCoincidenceHeight subscribed
1839+ SubscriberHold _ -> return ()
1178− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1840+ SubscriberHoldIdentity _ -> return ()
1841+ SubscriberSwitch subscribed -> do
1842+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1843+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1844+ when (oldHeight /= invalidHeight) $ do
1845+ writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1846+ WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1847+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1848+ SubscriberCoincidenceOuter subscribed -> do
1849+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1850+ invalidateCoincidenceHeight subscribed
1851+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1852+ SubscriberCoincidenceInner subscribed -> do
1853+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1854+ invalidateCoincidenceHeight subscribed
1855+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1856+ SubscriberHandle -> return ()
1857+
1858+removeMergeHeight :: Height -> MergeSubscribed k -> IO ()
1859+removeMergeHeight oldParentHeight subscribed = do
1860+ when debugInvalidateHeight $ putStrLn $ "removeMergeHeight: " <> show oldParentHeight <> " Merge" <> showNodeId subscribed
1861+ oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1862+ let newHeights = heightBagRemove oldParentHeight oldHeights
1863+ writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1864+
1865+invalidateMergeHeight :: MergeSubscribed k -> IO ()
1866+invalidateMergeHeight subscribed = do
1867+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1868+ -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1869+ when (oldHeight /= invalidHeight) $ do
1870+ writeIORef (mergeSubscribedHeight subscribed) $! invalidHeight
1871+ WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1872+
1873+addMergeHeight :: Height -> MergeSubscribed k -> IO ()
1874+addMergeHeight newParentHeight subscribed = do
1875+ when debugInvalidateHeight $ putStrLn $ "addMergeHeight: " <> show newParentHeight <> " Merge" <> showNodeId subscribed
1876+ oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1877+ let newHeights = heightBagAdd newParentHeight oldHeights
1878+ writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1879+
1880+revalidateMergeHeight :: MergeSubscribed k -> IO ()
1881+revalidateMergeHeight subscribed = do
1882+ currentHeight <- readIORef $ mergeSubscribedHeight subscribed
1883+ when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1884+ heights <- readIORef $ mergeSubscribedHeightBag subscribed
1885+ parents <- readIORef $ mergeSubscribedParents subscribed
1886+ -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1887+ case heightBagSize heights `compare` DMap.size parents of
1888+ LT -> return ()
1889+ EQ -> do
1890+ let height = succHeight $ heightBagMax heights
1891+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1892+ writeIORef (mergeSubscribedHeight subscribed) $! height
1893+ WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1894+ GT -> error $ "revalidateMergeHeight: more heights than parents for Merge" <> showNodeId subscribed
1179
1895
1180invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1896invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1181invalidateCoincidenceHeight subscribed = do
1897invalidateCoincidenceHeight subscribed = do
1182 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1898 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1183 when (oldHeight /= invalidHeight) $ do
1899 when (oldHeight /= invalidHeight) $ do
1184− writeIORef (coincidenceSubscribedHeight subscribed) $ invalidHeight
1900+ writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1185− mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
1901+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1186−
1902+
1187−--TODO: The recalculation algorithm seems a bit funky; make sure it doesn't miss stuff or hit stuff twice; also, it should probably be lazy
1903+recalculateSubscriberHeight :: Height -> Subscriber a -> IO ()
1188−
1904+recalculateSubscriberHeight new s = case s of
1189−recalculateSubscriberHeight :: WeakSubscriber a -> IO ()
1905+ SubscriberPush _ subscribed -> do
1190−recalculateSubscriberHeight ws = do
1906+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1191− ms <- deRefWeakSubscriber ws
1907+ WeakBag.traverse (pushSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1192− case ms of
1908+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1193− Nothing -> return () --TODO: cleanup?
1909+ SubscriberMerge _ subscribed -> do
1194− Just s -> case s of
1910+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1195− SubscriberPush _ subscribed -> do
1911+ addMergeHeight new subscribed
1196− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1912+ revalidateMergeHeight subscribed
1197− mapM_ recalculateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1913+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1198− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1914+ SubscriberMergeChange _ -> return ()
1199− SubscriberMerge _ subscribed -> do
1915+ SubscriberFan subscribed -> do
1200− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1916+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1201− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1917+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1202− when (oldHeight == invalidHeight) $ do
1918+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
1203− height <- calculateMergeHeight subscribed
1919+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1204− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1920+ SubscriberHold _ -> return ()
1205− when (height /= invalidHeight) $ do -- If height == invalidHeight, that means some of the prereqs have not yet been recomputed; when they do recompute, they'll catch this node again --TODO: this is O(n*m), where n is the number of children of this noe and m is the number that have been invalidated
1921+ SubscriberHoldIdentity _ -> return ()
1206− writeIORef (mergeSubscribedHeight subscribed) height
1922+ SubscriberSwitch subscribed -> do
1207− mapM_ recalculateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1923+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1208− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1924+ updateSwitchHeight new subscribed
1209− SubscriberFan subscribed -> do
1925+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1210− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1926+ SubscriberCoincidenceOuter subscribed -> do
1211− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1927+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1212− forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ recalculateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
1928+ recalculateCoincidenceHeight subscribed
1213− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1929+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1214− SubscriberHold _ -> return ()
1930+ SubscriberCoincidenceInner subscribed -> do
1215− SubscriberSwitch subscribed -> do
1931+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1216− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1932+ recalculateCoincidenceHeight subscribed
1217− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1933+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1218− when (oldHeight == invalidHeight) $ do
1934+ SubscriberHandle -> return ()
1219− height <- calculateSwitchHeight subscribed
1935+
1220− when (height /= invalidHeight) $ do
1936+updateSwitchHeight :: Height -> SwitchSubscribed a -> IO ()
1221− writeIORef (switchSubscribedHeight subscribed) height
1937+updateSwitchHeight new subscribed = do
1222− mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1938+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1223− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1939+ when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1224− SubscriberCoincidenceOuter subscribed -> do
1940+ when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1225− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1941+ writeIORef (switchSubscribedHeight subscribed) $! new
1226− void $ recalculateCoincidenceHeight subscribed
1942+ WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1227− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1228− SubscriberCoincidenceInner subscribed -> do
1229− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1230− void $ recalculateCoincidenceHeight subscribed
1231− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1232
1943
1233recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1944recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1234recalculateCoincidenceHeight subscribed = do
1945recalculateCoincidenceHeight subscribed = do
1235 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1946 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1236− when (oldHeight == invalidHeight) $ do
1947+ when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1237 height <- calculateCoincidenceHeight subscribed
1948 height <- calculateCoincidenceHeight subscribed
1238 when (height /= invalidHeight) $ do
1949 when (height /= invalidHeight) $ do
1239− writeIORef (coincidenceSubscribedHeight subscribed) height
1950+ writeIORef (coincidenceSubscribedHeight subscribed) $! height
1240− mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed) --TODO: This should probably be mandatory, just like with the merge and switch ones
1951+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1241
1952
1242−calculateMergeHeight :: MergeSubscribed k -> IO Int
1953+getEventSubscribedHeight :: EventSubscribed a -> IO Height
1243−calculateMergeHeight subscribed = if DMap.null (mergeSubscribedParents subscribed) then return 0 else do
1954+getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
1244− heights <- forM (DMap.toList $ mergeSubscribedParents subscribed) $ \(_ :=> es) -> do
1245− readIORef $ eventSubscribedHeightRef es
1246− return $ if any (== invalidHeight) heights then invalidHeight else succ $ Prelude.maximum heights --TODO: Replace 'any' with invalidHeight-preserving 'maximum'
1247
1955
1248−calculateSwitchHeight :: SwitchSubscribed a -> IO Int
1956+calculateSwitchHeight :: SwitchSubscribed a -> IO Height
1249−calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef =<< readIORef (switchSubscribedCurrentParent subscribed)
1957+calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1250
1958
1251−calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Int
1959+calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Height
1252calculateCoincidenceHeight subscribed = do
1960calculateCoincidenceHeight subscribed = do
1253− outerHeight <- readIORef $ eventSubscribedHeightRef $ coincidenceSubscribedOuterParent subscribed
1961+ outerHeight <- readIORef $ eventSubscribedHeightRef $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1254− innerHeight <- maybe (return 0) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1962+ innerHeight <- maybe (return zeroHeight) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1255 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1963 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1256
1964
1257−{-
1965+data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed a)
1258−recalculateEventSubscribedHeight :: EventSubscribed a -> IO Int
1259−recalculateEventSubscribedHeight es = case es of
1260− EventSubscribedRoot _ -> return 0
1261− EventSubscribedNever -> return 0
1262− EventSubscribedPush subscribed -> recalculateEventSubscribedHeight $ pushSubscribedParent subscribed
1263− EventSubscribedMerge subscribed -> do
1264− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1265− if oldHeight /= invalidHeight then return oldHeight else do
1266− height <- calculateMergeHeight subscribed
1267− writeIORef (mergeSubscribedHeight subscribed) height
1268− return height
1269− EventSubscribedFan _ subscribed -> recalculateEventSubscribedHeight $ fanSubscribedParent subscribed
1270− EventSubscribedSwitch subscribed -> do
1271− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1272− if oldHeight /= invalidHeight then return oldHeight else do
1273− height <- calculateSwitchHeight subscribed
1274− writeIORef (switchSubscribedHeight subscribed) height
1275− return height
1276− EventSubscribedCoincidence subscribed -> recalculateCoincidenceHeight subscribed
1277−-}
1278−
1279−data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed (SwitchSubscribed a)
1280−
1281−debugInvalidate :: Bool
1282−debugInvalidate = False
1283
1966
1284invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1967invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1285invalidate toReconnectRef wis = do
1968invalidate toReconnectRef wis = do
@@ -1287,19 +1970,19 @@ invalidate toReconnectRef wis = do
1287 mi <- deRefWeak wi
1970 mi <- deRefWeak wi
1288 case mi of
1971 case mi of
1289 Nothing -> do
1972 Nothing -> do
1290− when debugInvalidate $ liftIO $ putStrLn "invalidate Dead"
1973+ traceInvalidate "invalidate Dead"
1291 return () --TODO: Should we clean this up here?
1974 return () --TODO: Should we clean this up here?
1292 Just i -> do
1975 Just i -> do
1293 finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1976 finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1294 case i of
1977 case i of
1295 InvalidatorPull p -> do
1978 InvalidatorPull p -> do
1296− when debugInvalidate $ liftIO $ putStrLn "invalidate Pull"
1979+ traceInvalidate $ "invalidate: Pull" <> showNodeId p
1297 mVal <- readIORef $ pullValue p
1980 mVal <- readIORef $ pullValue p
1298 forM_ mVal $ \val -> do
1981 forM_ mVal $ \val -> do
1299 writeIORef (pullValue p) Nothing
1982 writeIORef (pullValue p) Nothing
1300− writeIORef (pullSubscribedInvalidators val) =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1983+ writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1301 InvalidatorSwitch subscribed -> do
1984 InvalidatorSwitch subscribed -> do
1302− when debugInvalidate $ liftIO $ putStrLn "invalidate Switch"
1985+ traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
1303 modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1986 modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1304 return [] -- Since we always finalize everything, always return an empty list --TODO: There are some things that will need to be re-subscribed every time; we should try to avoid finalizing them
1987 return [] -- Since we always finalize everything, always return an empty list --TODO: There are some things that will need to be re-subscribed every time; we should try to avoid finalizing them
1305
1988
@@ -1307,83 +1990,228 @@ invalidate toReconnectRef wis = do
1307-- Reflex integration
1990-- Reflex integration
1308--------------------------------------------------------------------------------
1991--------------------------------------------------------------------------------
1309
1992
1310−data Spider
1993+-- | The default, global Spider environment
1311−
1994+type Spider = SpiderEnv Global
1312−instance R.Reflex Spider where
1313− newtype Behavior Spider a = SpiderBehavior { unSpiderBehavior :: Behavior a }
1314− newtype Event Spider a = SpiderEvent { unSpiderEvent :: Event a }
1315− type PullM Spider = BehaviorM
1316− type PushM Spider = EventM
1317− {-# INLINE never #-}
1318− {-# INLINE constant #-}
1319− never = SpiderEvent EventNever
1320− constant = SpiderBehavior . BehaviorConst
1321− push f = SpiderEvent. push f . unSpiderEvent
1322− pull = SpiderBehavior . pull
1323− merge = SpiderEvent . merge . (unsafeCoerce :: DMap k (R.Event Spider) -> DMap k Event)
1324− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
1325− switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event Spider a) -> Behavior (Event a)) . unSpiderBehavior
1326− coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event Spider a) -> Event (Event a)) . unSpiderEvent
1327
1995
1328−instance R.MonadSample Spider SpiderHost where
1996+-- | Designates the default, global Spider timeline
1329− {-# INLINE sample #-}
1997+data Global
1330− sample = SpiderHost . readBehavior . unSpiderBehavior
1331
1998
1332−instance R.MonadHold Spider SpiderHost where
1999+{-# NOINLINE globalSpiderEnv #-}
1333− hold v0 = SpiderHost . liftM SpiderBehavior . runFrame . hold v0 . unSpiderEvent
2000+globalSpiderEnv :: SpiderEnv Global
2001+globalSpiderEnv = unsafePerformIO unsafeNewSpiderEnv
1334
2002
1335−instance R.MonadSample Spider BehaviorM where
2003+-- | Stores all global data relevant to a particular Spider timeline; only one
1336− {-# INLINE sample #-}
2004+-- value should exist for each type @x@
1337− sample = readBehaviorTracked . unSpiderBehavior
2005+data SpiderEnv x = SpiderEnv
2006+ { _spiderEnv_toUnsubscribe :: !(IORef [SomeEventSubscription])
2007+ , _spiderEnv_lock :: !(MVar ())
2008+ , _spiderEnv_cleanups :: !(IORef [IO ()])
2009+#ifdef DEBUG
2010+ , _spiderEnv_depth :: !(IORef Int)
2011+#endif
2012+ }
2013+type role SpiderEnv nominal
2014+
2015+instance GEq SpiderEnv where
2016+ a `geq` b = if _spiderEnv_toUnsubscribe a == _spiderEnv_toUnsubscribe b
2017+ then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderEnv can't have two different 'x' arguments
2018+ else Nothing
2019+
2020+unsafeNewSpiderEnv :: forall x. IO (SpiderEnv x)
2021+unsafeNewSpiderEnv = do
2022+ toUnsubscribe <- newIORef []
2023+ lock <- newMVar ()
2024+ cleanups <- newIORef []
2025+#ifdef DEBUG
2026+ depthRef <- newIORef 0
2027+#endif
2028+ return $ SpiderEnv
2029+ { _spiderEnv_toUnsubscribe = toUnsubscribe
2030+ , _spiderEnv_lock = lock
2031+ , _spiderEnv_cleanups = cleanups
2032+#ifdef DEBUG
2033+ , _spiderEnv_depth = depthRef
2034+#endif
2035+ }
1338
2036
1339−instance R.MonadSample Spider EventM where
2037+-- | Create a new SpiderEnv
1340− {-# INLINE sample #-}
2038+newSpiderEnv :: IO (Some SpiderEnv)
1341− sample = liftIO . readBehavior . unSpiderBehavior
2039+newSpiderEnv = Some.This <$> unsafeNewSpiderEnv
1342
2040
1343−instance R.MonadHold Spider EventM where
2041+newtype SpiderPullM x a = SpiderPullM (BehaviorM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
1344− {-# INLINE hold #-}
1345− hold v0 e = SpiderBehavior <$> hold v0 (unSpiderEvent e)
1346
2042
1347−data RootTrigger a = forall k. GCompare k => RootTrigger (IORef [WeakSubscriber a], IORef (DMap k Identity), k a)
2043+newtype SpiderPushM x a = SpiderPushM (ComputeM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
1348−newtype SpiderEventHandle a = SpiderEventHandle { unEventHandle :: Event a }
1349
2044
1350−instance R.MonadSubscribeEvent Spider SpiderHostFrame where
2045+instance R.Reflex (SpiderEnv x) where
2046+ newtype Behavior (SpiderEnv x) a = SpiderBehavior { unSpiderBehavior :: Behavior a }
2047+ newtype Event (SpiderEnv x) a = SpiderEvent { unSpiderEvent :: Event a }
2048+ newtype Dynamic (SpiderEnv x) a = SpiderDynamic { unSpiderDynamic :: Dynamic Identity a } deriving (Functor, Applicative, Monad)
2049+ newtype Incremental (SpiderEnv x) p a = SpiderIncremental { unSpiderIncremental :: Dynamic p a }
2050+ type PullM (SpiderEnv x) = SpiderPullM x
2051+ type PushM (SpiderEnv x) = SpiderPushM x
2052+ {-# INLINABLE never #-}
2053+ never = SpiderEvent EventNever
2054+ {-# INLINABLE constant #-}
2055+ constant = SpiderBehavior . BehaviorConst
2056+ {-# INLINABLE push #-}
2057+ push f = SpiderEvent . push (coerce f) . unSpiderEvent
2058+ {-# INLINABLE pull #-}
2059+ pull = SpiderBehavior . pull . coerce
2060+ {-# INLINABLE merge #-}
2061+ merge = SpiderEvent . merge . DynamicConst . (unsafeCoerce :: DMap k (R.Event (SpiderEnv x)) -> DMap k Event)
2062+ {-# INLINABLE fan #-}
2063+ fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2064+ {-# INLINABLE switch #-}
2065+ switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event (SpiderEnv x) a) -> Behavior (Event a)) . unSpiderBehavior
2066+ {-# INLINABLE coincidence #-}
2067+ coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event (SpiderEnv x) a) -> Event (Event a)) . unSpiderEvent
2068+ {-# INLINABLE current #-}
2069+ current = SpiderBehavior . current . unSpiderDynamic
2070+ {-# INLINABLE updated #-}
2071+ updated = SpiderEvent . fmap runIdentity . updated . unSpiderDynamic
2072+ {-# INLINABLE unsafeBuildDynamic #-}
2073+ unsafeBuildDynamic readV0 v' = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (coerce readV0) $ addIdentity $ unSpiderEvent v'
2074+ {-# INLINABLE unsafeBuildIncremental #-}
2075+ unsafeBuildIncremental readV0 dv = SpiderIncremental $ DynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
2076+ {-# INLINABLE mergeIncremental #-}
2077+ mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic R.PatchDMap (DMap k (R.Event (SpiderEnv x))) -> Dynamic R.PatchDMap (DMap k Event)) . unSpiderIncremental
2078+ {-# INLINABLE currentIncremental #-}
2079+ currentIncremental = SpiderBehavior . current . unSpiderIncremental
2080+ {-# INLINABLE updatedIncremental #-}
2081+ updatedIncremental = SpiderEvent . updated . unSpiderIncremental
2082+ {-# INLINABLE incrementalToDynamic #-}
2083+ incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ current i) $ flip push (updated i) $ \p -> do
2084+ c <- readBehaviorUntracked $ current i
2085+ return $ Identity <$> R.apply p c --TODO: Avoid the redundant 'apply'
2086+
2087+instance R.MonadSample (SpiderEnv x) (EventM x) where
2088+ {-# INLINABLE sample #-}
2089+ sample (SpiderBehavior b) = readBehaviorUntracked b
2090+
2091+instance R.MonadHold (SpiderEnv x) (EventM x) where
2092+ {-# INLINABLE hold #-}
2093+ hold = holdSpiderEventM
2094+ {-# INLINABLE holdDyn #-}
2095+ holdDyn = holdDynSpiderEventM
2096+ {-# INLINABLE holdIncremental #-}
2097+ holdIncremental = holdIncrementalSpiderEventM
2098+
2099+holdSpiderEventM :: a -> R.Event (SpiderEnv x) a -> EventM x (R.Behavior (SpiderEnv x) a)
2100+holdSpiderEventM v0 e = fmap (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2101+
2102+holdDynSpiderEventM :: a -> R.Event (SpiderEnv x) a -> EventM x (R.Dynamic (SpiderEnv x) a)
2103+holdDynSpiderEventM v0 e = fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2104+
2105+holdIncrementalSpiderEventM :: R.Patch p => a -> R.Event (SpiderEnv x) (p a) -> EventM x (R.Incremental (SpiderEnv x) p a)
2106+holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
2107+
2108+instance R.MonadHold (SpiderEnv x) (SpiderHost x) where
2109+ {-# INLINABLE hold #-}
2110+ hold v0 e = R.runHostFrame $ R.hold v0 e
2111+ {-# INLINABLE holdDyn #-}
2112+ holdDyn v0 e = R.runHostFrame $ R.holdDyn v0 e
2113+ {-# INLINABLE holdIncremental #-}
2114+ holdIncremental v0 e = R.runHostFrame $ R.holdIncremental v0 e
2115+
2116+instance R.MonadSample (SpiderEnv x) (SpiderHost x) where
2117+ {-# INLINABLE sample #-}
2118+ sample = runFrame . readBehaviorUntracked . unSpiderBehavior
2119+
2120+instance R.MonadSample (SpiderEnv x) (SpiderPullM x) where
2121+ {-# INLINABLE sample #-}
2122+ sample = coerce . readBehaviorTracked . unSpiderBehavior
2123+
2124+instance R.MonadSample (SpiderEnv x) (SpiderPushM x) where
2125+ {-# INLINABLE sample #-}
2126+ sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
2127+
2128+instance R.MonadHold (SpiderEnv x) (SpiderPushM x) where
2129+ {-# INLINABLE hold #-}
2130+ hold v0 e = R.current <$> R.holdDyn v0 e
2131+ {-# INLINABLE holdDyn #-}
2132+ holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity e
2133+ {-# INLINABLE holdIncremental #-}
2134+ holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . DynamicHold <$> hold v0 e
2135+
2136+data RootTrigger a = forall k. GCompare k => RootTrigger (SubscriberList a, IORef (DMap k Identity), k a)
2137+newtype SpiderEventHandle a = SpiderEventHandle { unSpiderEventHandle :: EventSubscription a }
2138+
2139+instance R.MonadSubscribeEvent (SpiderEnv x) (SpiderHostFrame x) where
2140+ {-# INLINABLE subscribeEvent #-}
1351 subscribeEvent e = SpiderHostFrame $ do
2141 subscribeEvent e = SpiderHostFrame $ do
1352− _ <- getEventSubscribed $ unSpiderEvent e --TODO: The result of this should actually be used
2142+ subscription <- subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
1353− return $ SpiderEventHandle (unSpiderEvent e)
2143+ return $ SpiderEventHandle subscription
1354−
2144+
1355−instance R.ReflexHost Spider where
2145+instance R.ReflexHost (SpiderEnv x) where
1356− type EventTrigger Spider = RootTrigger
2146+ type EventTrigger (SpiderEnv x) = RootTrigger
1357− type EventHandle Spider = SpiderEventHandle
2147+ type EventHandle (SpiderEnv x) = SpiderEventHandle
1358− type HostFrame Spider = SpiderHostFrame
2148+ type HostFrame (SpiderEnv x) = SpiderHostFrame x
1359−
2149+
1360−instance R.MonadReadEvent Spider ReadPhase where
2150+instance R.MonadReadEvent (SpiderEnv x) (ReadPhase x) where
1361− {-# INLINE readEvent #-}
2151+ {-# INLINABLE readEvent #-}
1362− readEvent = ReadPhase . liftM (fmap return) . readEvent . unEventHandle
2152+ readEvent h = ReadPhase $ fmap (fmap return) $ liftIO $ do
1363−
2153+ let EventSubscription sln subscribed = unSpiderEventHandle h
1364−instance MonadRef EventM where
2154+ result <- readEventSubscribed subscribed
1365− type Ref EventM = Ref IO
2155+ touch sln
1366− {-# INLINE newRef #-}
2156+ return result
1367− {-# INLINE readRef #-}
2157+
1368− {-# INLINE writeRef #-}
2158+instance MonadRef (EventM x) where
2159+ type Ref (EventM x) = Ref IO
2160+ {-# INLINABLE newRef #-}
2161+ {-# INLINABLE readRef #-}
2162+ {-# INLINABLE writeRef #-}
1369 newRef = liftIO . newRef
2163 newRef = liftIO . newRef
1370 readRef = liftIO . readRef
2164 readRef = liftIO . readRef
1371 writeRef r a = liftIO $ writeRef r a
2165 writeRef r a = liftIO $ writeRef r a
1372
2166
1373−instance MonadAtomicRef EventM where
2167+instance MonadAtomicRef (EventM x) where
1374− {-# INLINE atomicModifyRef #-}
2168+ {-# INLINABLE atomicModifyRef #-}
1375 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2169 atomicModifyRef r f = liftIO $ atomicModifyRef r f
1376
2170
1377−newtype SpiderHost a = SpiderHost { runSpiderHost :: IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
2171+-- | The monad for actions that manipulate a Spider timeline identified by @x@
1378−
2172+newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderEnv x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
1379−newtype SpiderHostFrame a = SpiderHostFrame { runSpiderHostFrame :: EventM a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
2173+
1380−
2174+instance Monad (SpiderHost x) where
1381−instance R.MonadSample Spider SpiderHostFrame where
2175+ {-# INLINABLE (>>=) #-}
1382− sample = SpiderHostFrame . R.sample --TODO: This can cause problems with laziness, so we should get rid of it if we can
2176+ SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
1383−
2177+ {-# INLINABLE (>>) #-}
1384−instance R.MonadHold Spider SpiderHostFrame where
2178+ SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
1385− {-# INLINE hold #-}
2179+ {-# INLINABLE return #-}
1386− hold v0 e = SpiderHostFrame $ R.hold v0 e
2180+ return x = SpiderHost $ return x
2181+ {-# INLINABLE fail #-}
2182+ fail s = SpiderHost $ fail s
2183+
2184+-- | Run an action affecting the global Spider timeline; this will be guarded by
2185+-- a mutex for that timeline
2186+runSpiderHost :: SpiderHost Global a -> IO a
2187+runSpiderHost (SpiderHost a) = runReaderT a globalSpiderEnv
2188+
2189+newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2190+
2191+instance Monad (SpiderHostFrame x) where
2192+ {-# INLINABLE (>>=) #-}
2193+ SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
2194+ {-# INLINABLE (>>) #-}
2195+ SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2196+ {-# INLINABLE return #-}
2197+ return x = SpiderHostFrame $ return x
2198+ {-# INLINABLE fail #-}
2199+ fail s = SpiderHostFrame $ fail s
2200+
2201+instance R.MonadSample (SpiderEnv x) (SpiderHostFrame x) where
2202+ sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
2203+
2204+addIdentity :: Event a -> Event (Identity a)
2205+--addIdentity = fmap Identity
2206+addIdentity = unsafeCoerce
2207+
2208+instance R.MonadHold (SpiderEnv x) (SpiderHostFrame x) where
2209+ {-# INLINABLE hold #-}
2210+ hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2211+ {-# INLINABLE holdDyn #-}
2212+ holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2213+ {-# INLINABLE holdIncremental #-}
2214+ holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
1387
2215
1388newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
2216newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
1389newEventWithTriggerIO f = do
2217newEventWithTriggerIO f = do
@@ -1401,44 +2229,60 @@ newFanEventWithTriggerIO f = do
1401 }
2229 }
1402 return $ EventSelector $ \k -> EventRoot k r
2230 return $ EventSelector $ \k -> EventRoot k r
1403
2231
1404−instance R.MonadReflexCreateTrigger Spider SpiderHost where
2232+instance R.MonadReflexCreateTrigger (SpiderEnv x) (SpiderHost x) where
1405− newEventWithTrigger = SpiderHost . liftM SpiderEvent . newEventWithTriggerIO
2233+ newEventWithTrigger = SpiderHost . lift . fmap SpiderEvent . newEventWithTriggerIO
1406− newFanEventWithTrigger f = SpiderHost $ do
2234+ newFanEventWithTrigger f = SpiderHost $ lift $ do
1407 es <- newFanEventWithTriggerIO f
2235 es <- newFanEventWithTriggerIO f
1408 return $ R.EventSelector $ SpiderEvent . select es
2236 return $ R.EventSelector $ SpiderEvent . select es
1409
2237
1410−instance R.MonadReflexCreateTrigger Spider SpiderHostFrame where
2238+instance R.MonadReflexCreateTrigger (SpiderEnv x) (SpiderHostFrame x) where
1411− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . liftM SpiderEvent . newEventWithTriggerIO
2239+ newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
1412 newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
2240 newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
1413 es <- newFanEventWithTriggerIO f
2241 es <- newFanEventWithTriggerIO f
1414 return $ R.EventSelector $ SpiderEvent . select es
2242 return $ R.EventSelector $ SpiderEvent . select es
1415
2243
1416−instance R.MonadSubscribeEvent Spider SpiderHost where
2244+instance R.MonadSubscribeEvent (SpiderEnv x) (SpiderHost x) where
1417− subscribeEvent e = SpiderHost $ do
2245+ {-# INLINABLE subscribeEvent #-}
1418− _ <- runFrame $ getEventSubscribed $ unSpiderEvent e --TODO: The result of this should actually be used
2246+ subscribeEvent = subscribeEventSpiderHost
1419− return $ SpiderEventHandle (unSpiderEvent e)
2247+
2248+subscribeEventSpiderHost :: R.Event (SpiderEnv x) a -> SpiderHost x (R.EventHandle (SpiderEnv x) a)
2249+subscribeEventSpiderHost e = do
2250+ subscription <- runFrame $ subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2251+ return $ SpiderEventHandle subscription
2252+
2253+newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2254+
2255+instance R.MonadSample (SpiderEnv x) (ReadPhase x) where
2256+ {-# INLINABLE sample #-}
2257+ sample = ReadPhase . R.sample
1420
2258
1421−newtype ReadPhase a = ReadPhase { runReadPhase :: ResultM a } deriving (Functor, Applicative, Monad, MonadFix, R.MonadSample Spider, R.MonadHold Spider)
2259+instance R.MonadHold (SpiderEnv x) (ReadPhase x) where
2260+ {-# INLINABLE hold #-}
2261+ hold v0 e = ReadPhase $ R.hold v0 e
2262+ {-# INLINABLE holdDyn #-}
2263+ holdDyn v0 e = ReadPhase $ R.holdDyn v0 e
2264+ {-# INLINABLE holdIncremental #-}
2265+ holdIncremental v0 e = ReadPhase $ R.holdIncremental v0 e
1422
2266
1423−instance R.MonadReflexHost Spider SpiderHost where
2267+instance R.MonadReflexHost (SpiderEnv x) (SpiderHost x) where
1424− type ReadPhase SpiderHost = ReadPhase
2268+ type ReadPhase (SpiderHost x) = ReadPhase x
1425− fireEventsAndRead es (ReadPhase a) = SpiderHost $ run es a
2269+ fireEventsAndRead es (ReadPhase a) = run es a
1426− runHostFrame = SpiderHost . runFrame . runSpiderHostFrame
2270+ runHostFrame = runFrame . runSpiderHostFrame
1427
2271
1428−instance MonadRef SpiderHost where
2272+instance MonadRef (SpiderHost x) where
1429− type Ref SpiderHost = Ref IO
2273+ type Ref (SpiderHost x) = Ref IO
1430 newRef = SpiderHost . newRef
2274 newRef = SpiderHost . newRef
1431 readRef = SpiderHost . readRef
2275 readRef = SpiderHost . readRef
1432 writeRef r = SpiderHost . writeRef r
2276 writeRef r = SpiderHost . writeRef r
1433
2277
1434−instance MonadAtomicRef SpiderHost where
2278+instance MonadAtomicRef (SpiderHost x) where
1435 atomicModifyRef r = SpiderHost . atomicModifyRef r
2279 atomicModifyRef r = SpiderHost . atomicModifyRef r
1436
2280
1437−instance MonadRef SpiderHostFrame where
2281+instance MonadRef (SpiderHostFrame x) where
1438− type Ref SpiderHostFrame = Ref IO
2282+ type Ref (SpiderHostFrame x) = Ref IO
1439 newRef = SpiderHostFrame . newRef
2283 newRef = SpiderHostFrame . newRef
1440 readRef = SpiderHostFrame . readRef
2284 readRef = SpiderHostFrame . readRef
1441 writeRef r = SpiderHostFrame . writeRef r
2285 writeRef r = SpiderHostFrame . writeRef r
1442
2286
1443−instance MonadAtomicRef SpiderHostFrame where
2287+instance MonadAtomicRef (SpiderHostFrame x) where
1444 atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2288 atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
test/Reflex/Bench/Focused.hs+34−25
@@ -1,33 +1,42 @@
1−{-# LANGUAGE RecursiveDo, ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE RecursiveDo #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE TupleSections #-}
10+{-# LANGUAGE TypeSynonymInstances #-}
2
11
3module Reflex.Bench.Focused where
12module Reflex.Bench.Focused where
4
13
5import Reflex
14import Reflex
6import Reflex.TestPlan
15import Reflex.TestPlan
7
16
8−import Control.Monad.Fix
17+import Control.Applicative
9import Control.Monad
18import Control.Monad
19+import Control.Monad.Fix
10import Control.Monad.Identity
20import Control.Monad.Identity
11−import Control.Applicative
21+import Data.Traversable (for, traverse)
12−--import Data.Foldable
13−import Data.Traversable (for)
14
22
15−import Data.Maybe
23+import qualified Data.Dependent.Map as DMap
16import Data.Map (Map)
24import Data.Map (Map)
17import qualified Data.Map as Map
25import qualified Data.Map as Map
18−import qualified Data.Dependent.Map as DMap
26+import Data.Maybe
19
27
20import Data.Functor.Misc
28import Data.Functor.Misc
21
29
22−import Data.Tuple
23import Data.List
30import Data.List
24import Data.List.Split
31import Data.List.Split
32+import Data.Tuple
25
33
26−import Data.Word
27import Data.Monoid
34import Data.Monoid
35+import Data.Word
28
36
37+import Control.DeepSeq
29
38
30−import Prelude hiding (sum, concat)
39+import Prelude hiding (concat, sum)
31
40
32
41
33mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
42mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
@@ -38,15 +47,15 @@ mergeTree n es | length es <= n = sum' es
38 subTrees = map sum' merges
47 subTrees = map sum' merges
39 sum' = fmap sum . mconcat . fmap (fmap (:[]) )
48 sum' = fmap sum . mconcat . fmap (fmap (:[]) )
40
49
41−mergeListDyn :: (Reflex t, MonadHold t m) => [Dynamic t a] -> m (Dynamic t [a])
50+mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
42−mergeListDyn dyns = traverse (mapDyn pure) dyns >>= mconcatDyn
51+mergeListDyn = mconcat . fmap (fmap pure)
43
52
44−mergeTreeDyn :: (Num a, Reflex t, MonadHold t m, MonadFix m) => Int -> [Dynamic t a] -> m (Dynamic t a)
53+mergeTreeDyn :: (Num a, Reflex t) => Int -> [Dynamic t a] -> Dynamic t a
45mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
54mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
46− | otherwise = traverse sumDyns merges >>= mergeTreeDyn n
55+ | otherwise = mergeTreeDyn n $ fmap sumDyns merges
47 where
56 where
48− merges = chunksOf n dyns
57+ merges = chunksOf n dyns
49− sumDyns ds = mergeListDyn ds >>= mapDyn sum
58+ sumDyns = fmap sum . mergeListDyn
50
59
51
60
52
61
@@ -93,7 +102,7 @@ fanMerge n e = leftmost $ s <$> [0..n - 1] where
93-- Dumb version of the above using simple fmapMaybe to filter
102-- Dumb version of the above using simple fmapMaybe to filter
94fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
103fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
95fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
104fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
96− s k = flip fmapMaybe e $ \j -> if (k == j `mod` n)
105+ s k = flip fmapMaybe e $ \j -> if k == j `mod` n
97 then Just k
106 then Just k
98 else Nothing
107 else Nothing
99
108
@@ -116,13 +125,13 @@ fmapChain :: (Functor f) => Word -> f Word -> f Word
116fmapChain = iter (fmap (+1))
125fmapChain = iter (fmap (+1))
117
126
118mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
127mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
119−mapDynChain = iterM (mapDyn (+1))
128+mapDynChain = iterM (return . fmap (+1))
120
129
121joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
122−joinDynChain = iterM (\d -> joinDyn <$> mapDyn (const d) d)
131+joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
123
132
124combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
133combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
125−combineDynChain = iterM (\d -> combineDyn (+) d d)
134+combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
126
135
127pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
136pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
128pingPong e1 e2 = do
137pingPong e1 e2 = do
@@ -170,7 +179,7 @@ switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
170-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
179-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
171
180
172switchMergeBehaviors :: forall a t m k. (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Behavior t a) -> m (Behavior t (Map k a))
181switchMergeBehaviors :: forall a t m k. (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Behavior t a) -> m (Behavior t (Map k a))
173−switchMergeBehaviors mapChanges = pull <$> joinMap <$> holdMap mapChanges
182+switchMergeBehaviors mapChanges = pull . joinMap <$> holdMap mapChanges
174 where joinMap m = traverse sample =<< sample m
183 where joinMap m = traverse sample =<< sample m
175
184
176-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
185-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
@@ -244,7 +253,7 @@ subscribing n frames =
244 ]
253 ]
245
254
246 where
255 where
247− testSub :: (Eq a, Show a) => String -> (forall t n. (Reflex t, MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> (String, TestCase)
256+ testSub :: (Eq a, Show a, NFData a) => String -> (forall t n. (Reflex t, MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> (String, TestCase)
248 testSub name test = testE name (switches frames test)
257 testSub name test = testE name (switches frames test)
249
258
250-- Set of benchmark to test specifically merging dynamic collections
259-- Set of benchmark to test specifically merging dynamic collections
@@ -300,9 +309,9 @@ dynamics n =
300 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
309 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
301 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
310 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
302 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
311 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
303− , testE "dense mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< dense
312+ , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
304− , testE "sparse mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< sparse
313+ , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
305− , testE "mergeDyn" $ fmap updated $ mapDyn sum =<< mergeListDyn =<< sparse
314+ , testE "mergeDyn" $ fmap (updated . fmap sum . mergeListDyn) sparse
306 ] where
315 ] where
307
316
308 d :: TestPlan t m => m (Dynamic t Word)
317 d :: TestPlan t m => m (Dynamic t Word)
test/Reflex/Plan/Pure.hs+10−2
@@ -1,4 +1,10 @@
1−{-# LANGUAGE GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving, ScopedTypeVariables #-}
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE GADTs #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
2
8
3module Reflex.Plan.Pure where
9module Reflex.Plan.Pure where
4
10
@@ -14,9 +20,9 @@ import qualified Data.IntMap as IntMap
14import Data.IntSet (IntSet)
20import Data.IntSet (IntSet)
15import qualified Data.IntSet as IntSet
21import qualified Data.IntSet as IntSet
16
22
17−import Data.Monoid
18import Data.Bifunctor
23import Data.Bifunctor
19import Data.Maybe
24import Data.Maybe
25+import Data.Monoid
20import Prelude
26import Prelude
21
27
22
28
@@ -31,6 +37,8 @@ liftPlan = PurePlan . lift
31
37
32instance MonadHold (Pure Int) PurePlan where
38instance MonadHold (Pure Int) PurePlan where
33 hold initial = liftPlan . hold initial
39 hold initial = liftPlan . hold initial
40+ holdDyn initial = liftPlan . holdDyn initial
41+ holdIncremental initial = liftPlan . holdIncremental initial
34
42
35instance MonadSample (Pure Int) PurePlan where
43instance MonadSample (Pure Int) PurePlan where
36 sample = liftPlan . sample
44 sample = liftPlan . sample
test/Reflex/Plan/Reflex.hs+27−13
@@ -1,4 +1,13 @@
1−{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE RankNTypes #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE StandaloneDeriving #-}
10+{-# LANGUAGE UndecidableInstances #-}
2module Reflex.Plan.Reflex
11module Reflex.Plan.Reflex
3 ( TestPlan(..)
12 ( TestPlan(..)
4 , runPlan
13 , runPlan
@@ -9,6 +18,7 @@ module Reflex.Plan.Reflex
9
18
10
19
11 , readSchedule
20 , readSchedule
21+ , readSchedule_
12 , testSchedule
22 , testSchedule
13 , readEvent'
23 , readEvent'
14 , makeDense
24 , makeDense
@@ -27,13 +37,15 @@ import Control.Monad
27import Control.Monad.Identity
37import Control.Monad.Identity
28import Control.Monad.State.Strict
38import Control.Monad.State.Strict
29
39
40+import Control.Monad.Ref
30import Data.Dependent.Sum (DSum (..))
41import Data.Dependent.Sum (DSum (..))
31−import Data.Monoid
32−import Data.Traversable (traverse, sequenceA)
33−import Data.Maybe
34import qualified Data.IntMap.Strict as IntMap
42import qualified Data.IntMap.Strict as IntMap
35−import Control.Monad.Ref
43+import Data.Maybe
44+import Data.Monoid
45+import Data.Traversable (sequenceA, traverse)
36
46
47+import Control.DeepSeq
48+import Control.Exception
37import Data.IntMap.Strict (IntMap)
49import Data.IntMap.Strict (IntMap)
38import Data.IORef
50import Data.IORef
39import System.Mem
51import System.Mem
@@ -75,7 +87,7 @@ instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => T
75firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
87firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
76firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
88firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
77
89
78−runPlan :: (MonadReflexHost t m, MonadIORef m) => Plan t a -> m (a, Schedule t)
90+runPlan :: (MonadReflexHost t m) => Plan t a -> m (a, Schedule t)
79runPlan (Plan p) = runHostFrame $ runStateT p mempty
91runPlan (Plan p) = runHostFrame $ runStateT p mempty
80
92
81
93
@@ -90,29 +102,31 @@ makeDense s = fromMaybe (emptyRange 0) $ do
90-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
102-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
91-- after the last event)
103-- after the last event)
92-- performGC is called at each frame to test for GC issues
104-- performGC is called at each frame to test for GC issues
93−testSchedule :: (MonadReflexHost t m, MonadIORef m) => Schedule t -> ReadPhase m a -> m (IntMap a)
105+testSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
94testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
106testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
95
107
96−readSchedule :: (MonadReflexHost t m, MonadIORef m) => Schedule t -> ReadPhase m a -> m (IntMap a)
108+readSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
97readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
109readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
98
110
99−triggerFrame :: (MonadReflexHost t m, MonadIORef m) => ReadPhase m a -> Int -> [Firing t] -> m a
111+readSchedule_ :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m ()
112+readSchedule_ schedule readResult = mapM_ (uncurry $ triggerFrame readResult) $ IntMap.toList schedule
113+
114+triggerFrame :: (MonadReflexHost t m, MonadIORef m, NFData a) => ReadPhase m a -> Int -> [Firing t] -> m a
100triggerFrame readResult _ occs = do
115triggerFrame readResult _ occs = do
101 triggers <- catMaybes <$> traverse firingTrigger occs
116 triggers <- catMaybes <$> traverse firingTrigger occs
102− fireEventsAndRead triggers readResult
117+ liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
103−
104
118
105readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
119readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
106readEvent' = readEvent >=> sequenceA
120readEvent' = readEvent >=> sequenceA
107
121
108
122
109-- Convenience functions for running tests producing Events/Behaviors
123-- Convenience functions for running tests producing Events/Behaviors
110−runTestB :: (MonadReflexHost t m, MonadIORef m) => Plan t (Behavior t a) -> m (IntMap a)
124+runTestB :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Behavior t a) -> m (IntMap a)
111runTestB p = do
125runTestB p = do
112 (b, s) <- runPlan p
126 (b, s) <- runPlan p
113 testSchedule s $ sample b
127 testSchedule s $ sample b
114
128
115−runTestE :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (IntMap (Maybe a))
129+runTestE :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Event t a) -> m (IntMap (Maybe a))
116runTestE p = do
130runTestE p = do
117 (e, s) <- runPlan p
131 (e, s) <- runPlan p
118 h <- subscribeEvent e
132 h <- subscribeEvent e
test/Reflex/Pure.hs+109−20
@@ -1,28 +1,43 @@
1−{- | This module provides a pure implementation of Reflex, which is intended
1+-- | This module provides a pure implementation of Reflex, which is intended to
2−to serve as a reference for the semantics of the Reflex class. All implementations
2+-- serve as a reference for the semantics of the Reflex class. All
3−of Reflex should produce the same results as this implementation, although performance
3+-- implementations of Reflex should produce the same results as this
4−and laziness/strictness may differ.
4+-- implementation, although performance and laziness/strictness may differ.
5−-}
5+
6+{-# LANGUAGE EmptyDataDecls #-}
7+{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE InstanceSigs #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeFamilies #-}
12+
13+-- There are two expected orphan instances in this module:
14+-- * MonadSample (Pure t) ((->) t)
15+-- * MonadHold (Pure t) ((->) t)
16+{-# OPTIONS_GHC -fno-warn-orphans #-}
6
17
7−{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, EmptyDataDecls, InstanceSigs #-}
8module Reflex.Pure where
18module Reflex.Pure where
9
19
10−import Reflex.Class
20+import Control.Applicative -- Necessary for GHC 7.8
11−import Data.Functor.Misc
21+import Control.Monad
12−import Data.Functor.Identity
13−import Data.MemoTrie
14import Data.Dependent.Map (DMap, GCompare)
22import Data.Dependent.Map (DMap, GCompare)
15import qualified Data.Dependent.Map as DMap
23import qualified Data.Dependent.Map as DMap
24+import Data.Functor.Identity
25+import Data.Maybe
26+import Data.MemoTrie
27+import Data.Monoid
28+import Reflex.Class
16
29
17data Pure t
30data Pure t
18
31
19-- | The Enum instance of t must be dense: for all x :: t, there must not exist
32-- | The Enum instance of t must be dense: for all x :: t, there must not exist
20−-- any y :: t such that pred x < y < x. The HasTrie instance will be used exclusively
33+-- any y :: t such that pred x < y < x. The HasTrie instance will be used
21−-- to memoize functions of t, not for any of its other capabilities.
34+-- exclusively to memoize functions of t, not for any of its other capabilities.
22instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
35instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
23
36
24 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
37 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
25− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
38+ newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
39+ newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
40+ newtype Incremental (Pure t) p a = Incremental { unIncremental :: t -> (a, Maybe (p a)) }
26
41
27 type PushM (Pure t) = (->) t
42 type PushM (Pure t) = (->) t
28 type PullM (Pure t) = (->) t
43 type PullM (Pure t) = (->) t
@@ -39,9 +54,14 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
39 pull :: PullM (Pure t) a -> Behavior (Pure t) a
54 pull :: PullM (Pure t) a -> Behavior (Pure t) a
40 pull = Behavior . memo
55 pull = Behavior . memo
41
56
42− merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
57+ -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
58+ -- warning because the GCompare instance is not used; however, removing the
59+ -- GCompare instance produces a different warning, due to that constraint
60+ -- being present in the original class definition
61+
62+ --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
43 merge events = Event $ memo $ \t ->
63 merge events = Event $ memo $ \t ->
44− let currentOccurrences = unwrapDMapMaybe (($ t) . unEvent) events
64+ let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
45 in if DMap.null currentOccurrences
65 in if DMap.null currentOccurrences
46 then Nothing
66 then Nothing
47 else Just currentOccurrences
67 else Just currentOccurrences
@@ -55,21 +75,90 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
55 coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
75 coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
56 coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
76 coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
57
77
58−instance Ord t => MonadSample (Pure t) ((->) t) where
78+ current :: Dynamic (Pure t) a -> Behavior (Pure t) a
79+ current d = Behavior $ \t -> fst $ unDynamic d t
80+
81+ updated :: Dynamic (Pure t) a -> Event (Pure t) a
82+ updated d = Event $ \t -> snd $ unDynamic d t
83+
84+ unsafeBuildDynamic :: PullM (Pure t) a -> Event (Pure t) a -> Dynamic (Pure t) a
85+ unsafeBuildDynamic readV0 v' = Dynamic $ \t -> (readV0 t, unEvent v' t)
86+
87+ -- See UNUSED_CONSTRAINT, above.
88+
89+ --unsafeBuildIncremental :: Patch p => PullM (Pure t) a -> Event (Pure t) (p
90+ --a) -> Incremental (Pure t) p a
91+ unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
92+
93+ mergeIncremental (Incremental f) = Event $ \t ->
94+ let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ f t
95+ in if DMap.null results
96+ then Nothing
97+ else Just results
98+
99+ currentIncremental (Incremental f) = Behavior $ \t -> fst $ f t
100+
101+ updatedIncremental (Incremental f) = Event $ \t -> snd $ f t
102+
103+ incrementalToDynamic (Incremental f) = Dynamic $ \t ->
104+ let (old, mPatch) = f t
105+ e = case mPatch of
106+ Nothing -> Nothing
107+ Just patch -> apply patch old
108+ in (old, e)
109+
110+instance Functor (Dynamic (Pure t)) where
111+ fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
112+ in (f cur, fmap f upd)
113+
114+instance Applicative (Dynamic (Pure t)) where
115+ pure a = Dynamic $ \_ -> (a, Nothing)
116+ (<*>) = ap
117+
118+instance Monad (Dynamic (Pure t)) where
119+ return = pure
120+ (x :: Dynamic (Pure t) a) >>= (f :: a -> Dynamic (Pure t) b) = Dynamic $ \t ->
121+ let (curX :: a, updX :: Maybe a) = unDynamic x t
122+ (cur :: b, updOuter :: Maybe b) = unDynamic (f curX) t
123+ (updInner :: Maybe b, updBoth :: Maybe b) = case updX of
124+ Nothing -> (Nothing, Nothing)
125+ Just nextX -> let (c, u) = unDynamic (f nextX) t
126+ in (Just c, u)
127+ in (cur, getFirst $ mconcat $ map First [updBoth, updOuter, updInner])
128+
129+instance MonadSample (Pure t) ((->) t) where
59
130
60 sample :: Behavior (Pure t) a -> (t -> a)
131 sample :: Behavior (Pure t) a -> (t -> a)
61 sample = unBehavior
132 sample = unBehavior
62
133
63instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
134instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
64
135
65− hold :: a -> Event (Pure t) a -> (t -> (Behavior (Pure t) a))
136+ hold :: a -> Event (Pure t) a -> t -> Behavior (Pure t) a
66 hold initialValue e initialTime = Behavior f
137 hold initialValue e initialTime = Behavior f
67 where f = memo $ \sampleTime ->
138 where f = memo $ \sampleTime ->
68 -- Really, the sampleTime should never be prior to the initialTime,
139 -- Really, the sampleTime should never be prior to the initialTime,
69− -- because that would mean the Behavior is being sampled before being created.
140+ -- because that would mean the Behavior is being sampled before
141+ -- being created.
142+ if sampleTime <= initialTime
143+ then initialValue
144+ else let lastTime = pred sampleTime
145+ in fromMaybe (f lastTime) $ unEvent e lastTime
146+
147+ holdDyn :: a -> Event (Pure t) a -> t -> Dynamic (Pure t) a
148+ holdDyn initialValue e initialTime =
149+ let Behavior f = hold initialValue e initialTime
150+ in Dynamic $ \t -> (f t, unEvent e t)
151+
152+ holdIncremental :: Patch p => a -> Event (Pure t) (p a) -> t -> Incremental (Pure t) p a
153+ holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
154+ where f = memo $ \sampleTime ->
155+ -- Really, the sampleTime should never be prior to the initialTime,
156+ -- because that would mean the Behavior is being sampled before
157+ -- being created.
70 if sampleTime <= initialTime
158 if sampleTime <= initialTime
71 then initialValue
159 then initialValue
72 else let lastTime = pred sampleTime
160 else let lastTime = pred sampleTime
161+ lastValue = f lastTime
73 in case unEvent e lastTime of
162 in case unEvent e lastTime of
74− Nothing -> f lastTime
163+ Nothing -> lastValue
75− Just x -> x
164+ Just x -> fromMaybe lastValue $ apply x lastValue
test/Reflex/Test.hs+6−1
@@ -1,4 +1,9 @@
1−{-# LANGUAGE ConstraintKinds, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
2
7
3module Reflex.Test
8module Reflex.Test
4 ( testAgreement
9 ( testAgreement
test/Reflex/Test/CrossImpl.hs+45−29
@@ -1,27 +1,40 @@
1−{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, BangPatterns, ConstraintKinds #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE ExistentialQuantification #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE FunctionalDependencies #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE LambdaCase #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE NoMonomorphismRestriction #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE TypeOperators #-}
14+{-# LANGUAGE UndecidableInstances #-}
2module Reflex.Test.CrossImpl (test) where
15module Reflex.Test.CrossImpl (test) where
3
16
4−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl, and)
17+import Prelude hiding (and, foldl, mapM, mapM_, sequence, sequence_)
5
18
19+import Control.Monad.Ref
6import Reflex.Class
20import Reflex.Class
7−import Reflex.Host.Class
8import Reflex.Dynamic
21import Reflex.Dynamic
9−import qualified Reflex.Spider.Internal as S
22+import Reflex.Host.Class
10import qualified Reflex.Pure as P
23import qualified Reflex.Pure as P
11−import Control.Monad.Ref
24+import qualified Reflex.Spider.Internal as S
12
25
13−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
26+import Control.Arrow (second, (&&&))
14−import qualified Data.Set as Set
27+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28+import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29+import Control.Monad.Writer hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
30+import Data.Dependent.Map (DSum (..))
31+import Data.Foldable
15import Data.Map.Strict (Map)
32import Data.Map.Strict (Map)
16import qualified Data.Map.Strict as Map
33import qualified Data.Map.Strict as Map
17−import Control.Arrow (second, (&&&))
34+import qualified Data.Set as Set
18import Data.Traversable
35import Data.Traversable
19−import Data.Foldable
20−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
21−import Control.Monad.Writer hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
22−import Data.Dependent.Map (DSum (..))
23−import System.Mem
24import System.Exit
36import System.Exit
37+import System.Mem
25
38
26import System.IO.Unsafe
39import System.IO.Unsafe
27
40
@@ -73,7 +86,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
73 outputs <- forM times $ \t -> do
86 outputs <- forM times $ \t -> do
74 forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
87 forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
75 bOutput <- sample b'
88 bOutput <- sample b'
76− eOutput <- liftM join $ forM (Map.lookup t eMap) $ \val -> do
89+ eOutput <- fmap join $ forM (Map.lookup t eMap) $ \val -> do
77 mret <- readRef reTrigger
90 mret <- readRef reTrigger
78 let firing = case mret of
91 let firing = case mret of
79 Just ret -> [ret :=> Identity val]
92 Just ret -> [ret :=> Identity val]
@@ -81,7 +94,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
81 fireEventsAndRead firing $ sequence =<< readEvent e'Handle
94 fireEventsAndRead firing $ sequence =<< readEvent e'Handle
82 liftIO performGC
95 liftIO performGC
83 return (t, (bOutput, eOutput))
96 return (t, (bOutput, eOutput))
84− return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
97+ return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
85
98
86tracePerf :: Show a => a -> b -> b
99tracePerf :: Show a => a -> b -> b
87tracePerf = flip const
100tracePerf = flip const
@@ -111,7 +124,7 @@ testCases =
111 b' <- hold "123" e
124 b' <- hold "123" e
112 return (b', e)
125 return (b', e)
113 , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
126 , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
114− e' <- liftM updated $ count e
127+ e' <- updated <$> count e
115 b' <- hold (0 :: Int) e'
128 b' <- hold (0 :: Int) e'
116 return (b', e')
129 return (b', e')
117 , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
130 , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -124,23 +137,19 @@ testCases =
124 return (b, e'')
137 return (b, e'')
125 , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
138 , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
126 let e' = flip pushAlways e $ const $ do
139 let e' = flip pushAlways e $ const $ do
127− let ea = fmap (const "a") e
140+ let eab = splitRecombineEvent e
128− let eb = fmap (const "b") e
141+ switch <$> hold eab never
129− let eab = leftmost [ea, eb]
130− liftM switch $ hold eab never
131 e'' = coincidence e'
142 e'' = coincidence e'
132 return (b, e'')
143 return (b, e'')
133 , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
144 , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
134 let e' = flip pushAlways e $ const $ do
145 let e' = flip pushAlways e $ const $ do
135− let ea = fmap (const "a") e
146+ let eab = splitRecombineEvent e
136− let eb = fmap (const "b") e
147+ switch <$> hold eab (fmap (const e) e)
137− let eab = leftmost [ea, eb]
138− liftM switch $ hold eab (fmap (const e) e)
139 e'' = coincidence e'
148 e'' = coincidence e'
140 return (b, e'')
149 return (b, e'')
141 , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
150 , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
142 let e' = leftmost [e, e]
151 let e' = leftmost [e, e]
143− e'' <- liftM switch $ hold e' (fmap (const e) e)
152+ e'' <- switch <$> hold e' (fmap (const e) e)
144 return (b, e'')
153 return (b, e'')
145 , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
154 , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
146 let e' = fmap (const e) e
155 let e' = fmap (const e) e
@@ -160,11 +169,11 @@ testCases =
160 return (b, e'')
169 return (b, e'')
161 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
170 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
162 let e' = leftmost [e, e]
171 let e' = leftmost [e, e]
163− e'' <- liftM switch $ hold never (fmap (const e') e)
172+ e'' <- switch <$> hold never (fmap (const e') e)
164 return (b, e'')
173 return (b, e'')
165 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
174 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
166 let e' = flip push e $ \_ -> do
175 let e' = flip push e $ \_ -> do
167− return . Just =<< onceE e
176+ Just <$> onceE e
168 e'' <- switchPromptly never e'
177 e'' <- switchPromptly never e'
169 return (b, e'')
178 return (b, e'')
170 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
179 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -173,7 +182,7 @@ testCases =
173 e'' <- switchPromptly never e'
182 e'' <- switchPromptly never e'
174 return (b, e'')
183 return (b, e'')
175 , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
184 , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
176− let e' = flip pushAlways e $ \_ -> return $ fmap id e
185+ let e' = flip pushAlways e $ \_ -> return e
177 e'' = coincidence e'
186 e'' = coincidence e'
178 return (b, e'')
187 return (b, e'')
179 , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
188 , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -212,12 +221,19 @@ testCases =
212 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
221 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
213 bb <- hold "b" e
222 bb <- hold "b" e
214 bd <- hold never . fmap (const e) =<< onceE e
223 bd <- hold never . fmap (const e) =<< onceE e
215− eOuter <- liftM (pushAlways sample . fmap (const bb)) $ onceE e
224+ eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
216 let eInner = switch bd
225 let eInner = switch bd
217 e' = leftmost [eOuter, eInner]
226 e' = leftmost [eOuter, eInner]
218 return (b, e')
227 return (b, e')
219 ]
228 ]
220
229
230+splitRecombineEvent :: Reflex t => Event t a -> Event t String
231+splitRecombineEvent e =
232+ let ea = "a" <$ e
233+ eb = "b" <$ e
234+ in leftmost [ea, eb]
235+
236+
221test :: IO ()
237test :: IO ()
222test = do
238test = do
223 results <- forM testCases $ \(name, TestCase inputs builder) -> do
239 results <- forM testCases $ \(name, TestCase inputs builder) -> do
test/Reflex/Test/Micro.hs+37−73
@@ -1,20 +1,23 @@
1−{-# LANGUAGE ConstraintKinds, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
2
7
3module Reflex.Test.Micro (testCases) where
8module Reflex.Test.Micro (testCases) where
4
9
5import Reflex
10import Reflex
6import Reflex.TestPlan
11import Reflex.TestPlan
7
12
8−import Data.Word
9−import Control.Monad
10import Control.Applicative
13import Control.Applicative
14+import Control.Monad
11import Data.Char
15import Data.Char
12−import Data.Monoid
13import Data.Functor.Misc
16import Data.Functor.Misc
17+import Data.Monoid
14
18
15import Data.Foldable
19import Data.Foldable
16
20
17−import Data.Map (Map)
18import qualified Data.Map as Map
21import qualified Data.Map as Map
19
22
20import Prelude
23import Prelude
@@ -22,32 +25,30 @@ import Prelude
22testCases :: [(String, TestCase)]
25testCases :: [(String, TestCase)]
23testCases =
26testCases =
24 [ testB "hold" $ hold "0" =<< events1
27 [ testB "hold" $ hold "0" =<< events1
28+
25 , testB "count" $ do
29 , testB "count" $ do
26− b <- current <$> (count =<< events2)
30+ b <- current <$> (count =<< events2)
27− return $ (+ (0::Int)) <$> b
31+ return $ (+ (0::Int)) <$> b
28
32
29 , testB "pull-1" $ do
33 , testB "pull-1" $ do
30 b <- hold "0" =<< events1
34 b <- hold "0" =<< events1
31− return (id <$> id <$> b)
35+ return (pull $ sample $ pull $ sample b)
32−
33
36
34 , testB "pull-2" $ do
37 , testB "pull-2" $ do
35 b1 <- behavior1
38 b1 <- behavior1
36− return (id <$> pull $ liftA2 (<>) (sample b1) (sample b1))
39+ return (pull $ liftA2 (<>) (sample b1) (sample b1))
37
40
38 , testB "pull-3" $ do
41 , testB "pull-3" $ do
39 b1 <- behavior1
42 b1 <- behavior1
40 b2 <- behavior2
43 b2 <- behavior2
41− return (id <$> pull $ liftA2 (<>) (sample b1) (sample b2))
44+ return (pull $ liftA2 (<>) (sample b1) (sample b2))
42
45
43 , testB "pull-4" $ do
46 , testB "pull-4" $ do
44− es <- planList ["a", "b", "c"]
47+ es <- planList ["a", "b", "c"]
45− e <- plan [(0, ())]
48+ e <- plan [(0, ())]
46−
49+ b <- hold (constant "") $
47− b <- hold (constant "") $
50+ pushAlways (const $ hold "z" es) e
48− pushAlways (const $ hold "z" es) e
51+ return (join b)
49−
50− return (join b)
51
52
52 , testE "tag-1" $ do
53 , testE "tag-1" $ do
53 b1 <- behavior1
54 b1 <- behavior1
@@ -66,9 +67,9 @@ testCases =
66
67
67 , testE "leftmost" $ liftA2 leftmost2 events1 events2
68 , testE "leftmost" $ liftA2 leftmost2 events1 events2
68
69
69− , testE "appendEvents-1" $ liftA2 appendEvents events1 events2
70+ , testE "appendEvents-1" $ liftA2 mappend events1 events2
70− , testE "appendEvents-2" $ liftA2 appendEvents events3 events2
71
71
72+ , testE "appendEvents-2" $ liftA2 mappend events3 events2
72
73
73 , testE "merge-1" $ do
74 , testE "merge-1" $ do
74 e <- events1
75 e <- events1
@@ -77,20 +78,17 @@ testCases =
77 , testE "merge-2" $ do
78 , testE "merge-2" $ do
78 e <- events1
79 e <- events1
79 let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
80 let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
80−
81 let ee = flip pushAlways e $ const $ return m
81 let ee = flip pushAlways e $ const $ return m
82 return $ coincidence ee
82 return $ coincidence ee
83
83
84− , testE "onceE-1" $ do
84+ , testE "headE-1" $ do
85 e <- events1
85 e <- events1
86− onceE $ leftmost [e, e]
86+ headE $ leftmost [e, e]
87
87
88−
88+ , testE "headE-2" $ do
89− , testE "onceE-2" $ do
90 e <- events1
89 e <- events1
91 b <- hold never (e <$ e)
90 b <- hold never (e <$ e)
92− onceE $ switch b
91+ headE $ switch b
93−
94
92
95 , testE "switch-1" $ do
93 , testE "switch-1" $ do
96 e <- events1
94 e <- events1
@@ -111,7 +109,6 @@ testCases =
111 e <- events1
109 e <- events1
112 switch <$> hold (deep e) (e <$ e)
110 switch <$> hold (deep e) (e <$ e)
113
111
114−
115 , testE "switch-5" $ do
112 , testE "switch-5" $ do
116 e <- events1
113 e <- events1
117 return $ coincidence $ flip pushAlways e $ const $
114 return $ coincidence $ flip pushAlways e $ const $
@@ -122,7 +119,6 @@ testCases =
122 return $ coincidence $ flip pushAlways e $ const $ do
119 return $ coincidence $ flip pushAlways e $ const $ do
123 switch <$> hold ("x" <$ e) (e <$ e)
120 switch <$> hold ("x" <$ e) (e <$ e)
124
121
125−
126 , testE "switchPromptly-1" $ do
122 , testE "switchPromptly-1" $ do
127 e <- events1
123 e <- events1
128 let e' = e <$ e
124 let e' = e <$ e
@@ -147,7 +143,7 @@ testCases =
147 , testE "switchPromptly-5" $ do
143 , testE "switchPromptly-5" $ do
148 e <- events1
144 e <- events1
149 switchPromptly never $ flip push e $
145 switchPromptly never $ flip push e $
150− const (Just <$> onceE e)
146+ const (Just <$> headE e)
151
147
152 , testE "switchPromptly-6" $ do
148 , testE "switchPromptly-6" $ do
153 e <- events1
149 e <- events1
@@ -157,7 +153,7 @@ testCases =
157 , testE "coincidence-1" $ do
153 , testE "coincidence-1" $ do
158 e <- events1
154 e <- events1
159 return $ coincidence $ flip pushAlways e $
155 return $ coincidence $ flip pushAlways e $
160− const $ return (id <$> e)
156+ const $ return e
161
157
162 , testE "coincidence-2" $ do
158 , testE "coincidence-2" $ do
163 e <- events1
159 e <- events1
@@ -172,7 +168,7 @@ testCases =
172 , testE "coincidence-4" $ do
168 , testE "coincidence-4" $ do
173 e <- events1
169 e <- events1
174 return $ coincidence $ flip pushAlways e $
170 return $ coincidence $ flip pushAlways e $
175− const (onceE e)
171+ const (headE e)
176
172
177 , testE "coincidence-5" $ do
173 , testE "coincidence-5" $ do
178 e <- events1
174 e <- events1
@@ -192,16 +188,16 @@ testCases =
192
188
193 , testB "holdWhileFiring" $ do
189 , testB "holdWhileFiring" $ do
194 e <- events1
190 e <- events1
195− eo <- onceE e
191+ eo <- headE e
196 bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
192 bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
197 return $ pull $ sample =<< sample bb
193 return $ pull $ sample =<< sample bb
198
194
199 , testE "joinDyn" $ do
195 , testE "joinDyn" $ do
200 e <- events1
196 e <- events1
201 bb <- hold "b" e
197 bb <- hold "b" e
202− bd <- hold never . fmap (const e) =<< onceE e
198+ bd <- hold never . fmap (const e) =<< headE e
203
199
204− eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
200+ eOuter <- pushAlways sample . fmap (const bb) <$> headE e
205 let eInner = switch bd
201 let eInner = switch bd
206 return $ leftmost [eOuter, eInner]
202 return $ leftmost [eOuter, eInner]
207
203
@@ -211,13 +207,13 @@ testCases =
211
207
212 , testB "mapDyn" $ do
208 , testB "mapDyn" $ do
213 d <- foldDyn (++) "0" =<< events1
209 d <- foldDyn (++) "0" =<< events1
214− current <$> mapDyn (map toUpper) d
210+ return $ current $ fmap (map toUpper) d
215
211
216 , testB "combineDyn" $ do
212 , testB "combineDyn" $ do
217 d1 <- foldDyn (++) "0" =<< events1
213 d1 <- foldDyn (++) "0" =<< events1
218− d2 <- mapDyn (map toUpper) =<< foldDyn (++) "0" =<< events2
214+ d2 <- fmap (fmap (map toUpper)) $ foldDyn (++) "0" =<< events2
219
215
220− current <$> combineDyn (<>) d1 d2
216+ return $ current $ zipDynWith (<>) d1 d2
221 , testE "fan-1" $ do
217 , testE "fan-1" $ do
222 e <- fmap toMap <$> events1
218 e <- fmap toMap <$> events1
223 let es = select (fanMap e) . Const2 <$> values
219 let es = select (fanMap e) . Const2 <$> values
@@ -231,7 +227,7 @@ testCases =
231 return (mergeList es)
227 return (mergeList es)
232
228
233 , testE "fan-3" $ do
229 , testE "fan-3" $ do
234− f <- fanMap <$> fmap toMap <$> events3
230+ f <- fanMap . fmap toMap <$> events3
235 return $ select f (Const2 'c')
231 return $ select f (Const2 'c')
236
232
237 , testE "fan-4" $ do
233 , testE "fan-4" $ do
@@ -243,52 +239,20 @@ testCases =
243 return $ toUpper <$> select (fanMap e) (Const2 'c')
239 return $ toUpper <$> select (fanMap e) (Const2 'c')
244
240
245 , testE "fan-6" $ do
241 , testE "fan-6" $ do
246− f <- fanMap <$> fmap toMap <$> events1
242+ f <- fanMap . fmap toMap <$> events1
247 return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
243 return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
248
244
249− -- , testE "switchMerge-1" $ switchMergeMap mempty =<< increasing
250−
251− -- , testE "switchMerge-2" $ do
252− -- e <- eventsFrom 0
253− -- switchMergeMap (Map.singleton 0 e) =<< increasing
254−
255− -- , testE "switchMerge-3" $ do
256− -- es <- eventsMany
257− -- switchMergeMap es =<< increasing
258−
259− -- , testE "switchMerge-4" $ do
260− -- es <- eventsMany
261− -- switchMergeMap es =<< decreasing
262−
263 ] where
245 ] where
264
246
265 events1, events2, events3 :: TestPlan t m => m (Event t String)
247 events1, events2, events3 :: TestPlan t m => m (Event t String)
266 events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
248 events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
267 events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
249 events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
268
250
269− events3 = liftA2 appendEvents events1 events2
251+ events3 = liftA2 mappend events1 events2
270−
271− eventsFrom :: TestPlan t m => Word -> m (Event t Int)
272− eventsFrom n = plan $ zip [n..8] [1..8]
273−
274− increasing :: TestPlan t m => m (Event t (Map Int (Event t Int)))
275− increasing = do
276− es <- mapM eventsFrom [1..8]
277− planList $ zipWith Map.singleton [1..8] es
278−
279− decreasing :: TestPlan t m => m (Event t (Map Int (Event t Int)))
280− decreasing = planList $ zipWith Map.singleton [1..8] (repeat never)
281−
282− eventsMany :: TestPlan t m => m (Map Int (Event t Int))
283− eventsMany = do
284− es <- mapM eventsFrom [8,7..1]
285− return $ Map.fromList $ zip [1..8] es
286−
287
252
288 values = "abcde"
253 values = "abcde"
289 toMap str = Map.fromList $ map (\c -> (c, c)) str
254 toMap str = Map.fromList $ map (\c -> (c, c)) str
290
255
291−
292 behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
256 behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
293 behavior1 = hold "1" =<< events1
257 behavior1 = hold "1" =<< events1
294 behavior2 = hold "2" =<< events2
258 behavior2 = hold "2" =<< events2
test/Reflex/Test/RunAll.hs+10−5
@@ -1,17 +1,22 @@
1−{-# LANGUAGE ConstraintKinds, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
2
7
3module Main (main) where
8module Main (main) where
4
9
5import Reflex.Test
10import Reflex.Test
6
11
7−import qualified Reflex.Bench.Focused as Focused
8−import qualified Reflex.Test.Micro as Micro
9−import Data.Functor
10import Data.Bifunctor
12import Data.Bifunctor
13+import Data.Functor
11import Data.List
14import Data.List
15+import qualified Reflex.Bench.Focused as Focused
16+import qualified Reflex.Test.Micro as Micro
12
17
13−import System.Exit
14import System.Environment
18import System.Environment
19+import System.Exit
15
20
16import Prelude
21import Prelude
17
22
test/Reflex/TestPlan.hs+14−6
@@ -1,4 +1,11 @@
1−{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE UndecidableInstances #-}
2module Reflex.TestPlan
9module Reflex.TestPlan
3 ( TestPlan(..)
10 ( TestPlan(..)
4
11
@@ -10,9 +17,10 @@ module Reflex.TestPlan
10
17
11 ) where
18 ) where
12
19
13−import Reflex.Class
20+import Control.DeepSeq
14import Control.Monad.Fix
21import Control.Monad.Fix
15import Data.Word
22import Data.Word
23+import Reflex.Class
16
24
17
25
18import Prelude
26import Prelude
@@ -32,13 +40,13 @@ type TestE a = forall t m. TestPlan t m => m (Event t a)
32type TestB a = forall t m. TestPlan t m => m (Behavior t a)
40type TestB a = forall t m. TestPlan t m => m (Behavior t a)
33
41
34data TestCase where
42data TestCase where
35− TestE :: (Show a, Eq a) => TestE a -> TestCase
43+ TestE :: (Show a, Eq a, NFData a) => TestE a -> TestCase
36− TestB :: (Show a, Eq a) => TestB a -> TestCase
44+ TestB :: (Show a, Eq a, NFData a) => TestB a -> TestCase
37
45
38-- Helpers to declare test cases
46-- Helpers to declare test cases
39−testE :: (Eq a, Show a) => String -> TestE a -> (String, TestCase)
47+testE :: (Eq a, Show a, NFData a) => String -> TestE a -> (String, TestCase)
40testE name test = (name, TestE test)
48testE name test = (name, TestE test)
41
49
42−testB :: (Eq a, Show a) => String -> TestB a -> (String, TestCase)
50+testB :: (Eq a, Show a, NFData a) => String -> TestB a -> (String, TestCase)
43testB name test = (name, TestB test)
51testB name test = (name, TestB test)
44
52
test/hlint.hsnew file+16
@@ -0,0 +1,16 @@
1+import Language.Haskell.HLint3 (hlint)
2+import System.Exit (exitFailure, exitSuccess)
3+
4+main :: IO ()
5+main = do
6+ ideas <- hlint
7+ [ "."
8+ , "--ignore=Redundant do"
9+ , "--ignore=Use camelCase"
10+ , "--ignore=Redundant $"
11+ , "--ignore=Use &&&"
12+ , "--ignore=Use const"
13+ , "--ignore=Use >=>"
14+ , "--ignore=Use ."
15+ ]
16+ if null ideas then exitSuccess else exitFailure