on develop help sign in
Merge remote-tracking branch 'origin/functor-dynamic' into develop

# Conflicts: # reflex.cabal

commit2401b744df336bd34f0a39d9dd8234dfe3787155 authorRyan Trinkle <ryan.trinkle@gmail.com> dateMon 1 Aug 2016, 17:19 (10 years ago) parent312b190bfe10ec
29 files changed+3545−1580
.ghci−2 .gitignore+2 .stylish-haskell.yaml+154 .travis.yml+3−1 Quickref.md+1 bench/Main.hs+33−35 bench/RunAll.hs+34−21 default.nix+3−1 reflex.cabal+26−11 src/Data/Functor/Misc.hs+134−41 src/Data/WeakBag.hs+120 src/Reflex.hs+8−9 src/Reflex/Class.hs+527−122 src/Reflex/Dynamic.hs+335−332 src/Reflex/Dynamic/TH.hs+32−11 src/Reflex/Dynamic/Uniq.hs+101 src/Reflex/Host/Class.hs+110−55 src/Reflex/Spider.hs+6−1 src/Reflex/Spider/Internal.hs+1608−764 test/Reflex/Bench/Focused.hs+34−25 test/Reflex/Plan/Pure.hs+10−2 test/Reflex/Plan/Reflex.hs+27−13 test/Reflex/Pure.hs+109−20 test/Reflex/Test.hs+6−1 test/Reflex/Test/CrossImpl.hs+45−29 test/Reflex/Test/Micro.hs+37−73 test/Reflex/Test/RunAll.hs+10−5 test/Reflex/TestPlan.hs+14−6 test/hlint.hs+16
.ghci−2
@@ -1,4 +1,2 @@
11:set -isrc
2−:set -hide-package MonadCatchIO-mtl
3−:set -hide-package monads-fd
42:seti -XOverloadedStrings
.gitignore+2
@@ -29,3 +29,5 @@ tags
2929hsenv.log
3030\#*#
3131.#*
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:
66 - travis_retry cabal-$CABALVER install --only-dependencies -j --enable-tests
77script:
88 - cabal-$CABALVER configure -v2 --enable-tests
9− - cabal-$CABALVER build
9+ - travis_wait cabal-$CABALVER build
1010 - cabal-$CABALVER test
1111 - cabal-$CABALVER sdist # tests that a source-distribution can be generated
1212# Check that the resulting source distribution can be built & installed.
@@ -21,4 +21,6 @@ matrix:
2121 addons: {apt: {packages: [cabal-install-1.18, ghc-7.8.4, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}
2222 - env: CABALVER=1.22 GHCVER=7.10.1
2323 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]}}
2426 fast_finish: true
Quickref.md+1
@@ -127,6 +127,7 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
127127[H] mconcatDyn :: Monoid a => [Dynamic a] -> m (Dynamic a)
128128[H] distributeDMapOverDyn :: GCompare k => DMap (WrapArg Dynamic k) -> m (Dynamic (DMap k))
129129[H] combineDyn :: (a -> b -> c) -> Dynamic a -> Dynamic b -> m (Dynamic c)
130+[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
130131
131132-- Efficient one-to-many fanout
132133[ ] demux :: Ord k => Dynamic k -> Demux k
bench/Main.hs+33−35
@@ -1,38 +1,36 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE TupleSections #-}
2+{-# LANGUAGE CPP #-}
33{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE TupleSections #-}
45{-# LANGUAGE TypeFamilies #-}
6+
7+-- The instance for NFData (TVar a) is an orphan, but necessary here
8+{-# OPTIONS_GHC -fno-warn-orphans #-}
9+
510module Main where
611
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
1212import Control.Concurrent.STM
13−import Control.Applicative
14−import System.IO.Unsafe
15−import Data.IORef
1613import Control.DeepSeq
1714import 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
1923import Reflex
2024import 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−
2925
3026main :: IO ()
3127main = defaultMain
3228 [ bgroup "micro" micros ]
3329
30+#if !(MIN_VERSION_deepseq(1,4,2))
3431instance NFData (IORef a) where
3532 rnf x = seq x ()
33+#endif
3634
3735instance NFData (TVar a) where
3836 rnf x = seq x ()
@@ -41,15 +39,14 @@ newtype WHNF a = WHNF a
4139instance NFData (WHNF a) where
4240 rnf (WHNF a) = seq a ()
4341
44−withSetup :: NFData b => String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
42+withSetup :: NFData b => String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
4543withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
4644 bench name . nfIO $ runSpiderHost (action a)
4745
48−withSetupWHNF :: String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
46+withSetupWHNF :: String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
4947withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
5048 bench name . whnfIO $ runSpiderHost (action a)
5149
52−
5350micros :: [Benchmark]
5451micros =
5552 [ bench "newIORef" $ whnfIO $ void $ newIORef ()
@@ -72,40 +69,41 @@ micros =
7269 (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
7370 , withSetupWHNF "fireEventsOnly"
7471 (newEventWithTriggerRef >>= subscribePair)
75− (\(subd, trigger) -> do
72+ (\(_, trigger) -> do
7673 Just key <- liftIO $ readIORef trigger
7774 fireEvents [key :=> Identity (42 :: Int)])
7875 , withSetupWHNF "fireEventsAndRead(head/merge1)"
7976 (setupMerge 1 >>= subscribePair)
80− (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
77+ (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
8178 , withSetupWHNF "fireEventsAndRead(head/merge100)"
8279 (setupMerge 100 >>= subscribePair)
83− (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
80+ (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
8481 , withSetupWHNF "fireEventsAndRead(head/merge10000)"
8582 (setupMerge 10000 >>= subscribePair)
86− (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
83+ (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
8784 , withSetupWHNF "fireEventsOnly(head/merge100)"
8885 (setupMerge 100 >>= subscribePair)
89− (\(subd, t:riggers) -> do
86+ (\(_, t:_) -> do
9087 Just key <- liftIO $ readIORef t
9188 fireEvents [key :=> Identity (42 :: Int)])
92− , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev,trigger) -> hold (42 :: Int) ev
89+ , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev, _) -> hold (42 :: Int) ev
9390 , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
9491 ]
9592
9693setupMerge :: Int
97− -> SpiderHost (Event Spider (DM.DMap (Const2 Int a) Identity),
98− [IORef (Maybe (EventTrigger Spider a))])
94+ -> SpiderHost Global ( Event (SpiderEnv Global) (DMap (Const2 Int a) Identity)
95+ , [IORef (Maybe (EventTrigger Spider a))]
96+ )
9997setupMerge num = do
100− (evs, triggers) <- unzip <$> replicateM 100 newEventWithTriggerRef
101− let !m = DM.fromList [(Const2 i) :=> v | (i,v) <- zip [0..] evs]
98+ (evs, triggers) <- unzip <$> replicateM num newEventWithTriggerRef
99+ let !m = DMap.fromList [Const2 i :=> v | (i,v) <- zip [0..] evs]
102100 pure (merge m, triggers)
103101
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)
105103subscribePair (ev, b) = (,b) <$> subscribeEvent ev
106104
107−fireAndRead :: IORef (Maybe (EventTrigger Spider a)) -> a -> EventHandle Spider b
108− -> SpiderHost (Maybe b)
105+fireAndRead :: IORef (Maybe (EventTrigger (SpiderEnv Global) a)) -> a -> EventHandle (SpiderEnv Global) b
106+ -> SpiderHost Global (Maybe b)
109107fireAndRead trigger val subd = do
110108 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 #-}
216{-# OPTIONS_GHC -fno-warn-orphans #-}
317
418module Main where
@@ -9,38 +23,37 @@ import Criterion.Types
923import Reflex
1024import Reflex.Host.Class
1125
12−import Reflex.TestPlan
1326import Reflex.Plan.Reflex
27+import Reflex.TestPlan
1428
15−import Reflex.Spider.Internal (SpiderEventHandle)
1629import qualified Reflex.Bench.Focused as Focused
30+import Reflex.Spider.Internal (SpiderEventHandle)
1731
1832import Control.Applicative
1933import Control.DeepSeq (NFData (..))
2034
35+import Prelude
2136import System.IO
2237import System.Mem
23−import Prelude
2438
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
3239import Control.Arrow
40+import Control.Concurrent
41+import Control.Concurrent.STM
42+import Control.Exception
3343import Control.Monad
44+import Control.Monad.Trans
3445import Data.Bool
35−import qualified GHC.Event as GHC
36−import Control.Concurrent.STM
46+import Data.Function
3747import Data.Int
38−import Control.Exception
39−import Text.Read
40−import System.Environment
48+import Data.IORef
49+import Data.Monoid
50+import Data.Time.Clock
4151import Debug.Trace.LocationTH
42−import Control.Monad.Trans
52+import GHC.Stats
53+import System.Environment
54+import System.Mem.Weak
4355import System.Process
56+import Text.Read
4457
4558import Unsafe.Coerce
4659
@@ -68,7 +81,7 @@ instance NFData (Behavior t a) where
6881 rnf !_ = ()
6982
7083instance NFData (Firing t) where
71− rnf !(Firing _ _) = ()
84+ rnf !_ = ()
7285
7386-- Measure the running time
7487benchFiring :: 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
8093 case tc of
8194 TestE p -> do
8295 (h, s) <- setupFiring p
83− runIterations $ readSchedule s $ readEvent' h
96+ runIterations $ readSchedule_ s $ readEvent' h
8497 TestB p -> do
8598 (b, s) <- runPlan p
86− runIterations $ readSchedule (makeDense s) $ sample b
99+ runIterations $ readSchedule_ (makeDense s) $ sample b
87100
88101waitForFinalizers :: IO ()
89102waitForFinalizers = do
@@ -94,7 +107,7 @@ waitForFinalizers = do
94107 performGC
95108 fix $ \loop -> do
96109 f <- readIORef isFinalized
97− if f then return () else do
110+ unless f $ do
98111 threadDelay 1
99112 loop
100113
default.nix+3−1
@@ -2,7 +2,7 @@
22, exception-transformers, haskell-src-exts, haskell-src-meta
33, MemoTrie, mtl, primitive, ref-tf, semigroups, stdenv, syb
44, template-haskell, these, transformers, transformers-compat
5−, criterion, deepseq, split, stm, loch-th
5+, criterion, deepseq, split, stm, loch-th, hlint
66}:
77mkDerivation {
88 pname = "reflex";
@@ -17,6 +17,8 @@ mkDerivation {
1717 base containers dependent-map MemoTrie mtl ref-tf
1818 # Benchmark dependencies
1919 criterion deepseq split stm loch-th
20+ # HLint
21+ hlint
2022 ];
2123 homepage = "https://github.com/reflex-frp/reflex";
2224 description = "Higher-order Functional Reactive Programming";
reflex.cabal+26−11
@@ -17,9 +17,11 @@ library
1717 hs-source-dirs: src
1818 build-depends:
1919 base >= 4.7 && < 4.10,
20+ bifunctors >= 5.2 && < 5.5,
2021 dependent-sum == 0.3.*,
21− dependent-map == 0.2.*,
22+ dependent-map >= 0.2.2 && < 0.3,
2223 semigroups >= 0.16 && < 0.19,
24+ semigroupoids >= 4.0 && < 6,
2325 mtl >= 2.1 && < 2.3,
2426 containers == 0.5.*,
2527 these >= 0.4 && < 0.8,
@@ -31,7 +33,8 @@ library
3133 transformers-compat >= 0.3,
3234 haskell-src-exts >= 1.16 && < 1.18,
3335 haskell-src-meta == 0.6.*,
34− syb >= 0.5 && < 0.7
36+ syb >= 0.5 && < 0.7,
37+ stm == 2.4.*
3538
3639 exposed-modules:
3740 Reflex,
@@ -39,18 +42,21 @@ library
3942 Reflex.Spider.Internal,
4043 Reflex.Class,
4144 Reflex.Dynamic,
45+ Reflex.Dynamic.Uniq,
4246 Reflex.Dynamic.TH,
4347 Reflex.Host.Class,
44− Data.Functor.Misc
48+ Data.Functor.Misc,
49+ Data.WeakBag
4550
4651 other-extensions: TemplateHaskell
4752 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
53+ ghc-prof-options: -auto-all
4854
4955
5056test-suite all
5157 type: exitcode-stdio-1.0
5258 main-is: Reflex/Test/RunAll.hs
53− ghc-options: -O2 -Wall
59+ ghc-options: -O2 -Wall -rtsopts
5460 hs-source-dirs: test
5561 build-depends:
5662 base,
@@ -60,8 +66,9 @@ test-suite all
6066 containers,
6167 dependent-map,
6268 dependent-sum,
63− transformers >= 0.3 && < 0.5,
69+ transformers >= 0.3,
6470 MemoTrie == 0.6.*,
71+ deepseq >= 1.3 && < 1.5,
6572 bifunctors,
6673 split
6774
@@ -70,33 +77,35 @@ benchmark spider-bench
7077 type: exitcode-stdio-1.0
7178 hs-source-dirs: bench
7279 main-is: Main.hs
73− ghc-options: -O2 -rtsopts
80+ ghc-options: -Wall -O2 -rtsopts
7481 build-depends:
7582 base,
7683 dependent-sum,
7784 dependent-map,
78− transformers >= 0.3 && < 0.5,
79− stm == 2.4.*,
85+ transformers >= 0.3,
86+ stm,
8087 deepseq >= 1.3 && < 1.5,
8188 mtl,
8289 primitive,
8390 criterion == 1.1.*,
8491 reflex
8592
93+
8694benchmark saulzar-bench
8795 type: exitcode-stdio-1.0
8896 hs-source-dirs: bench test
8997 c-sources: bench-cbits/checkCapability.c
9098 main-is: RunAll.hs
91− ghc-options: -O2 -rtsopts -threaded
99+ ghc-options: -Wall -O2 -rtsopts -threaded
100+ ghc-prof-options: -auto-all
92101 build-depends:
93102 base,
94103 containers == 0.5.*,
95104 ref-tf == 0.4,
96105 dependent-sum,
97106 dependent-map,
98− transformers >= 0.3 && < 0.5,
99− stm == 2.4.*,
107+ transformers >= 0.3,
108+ stm,
100109 deepseq >= 1.3 && < 1.5,
101110 mtl,
102111 primitive,
@@ -107,6 +116,12 @@ benchmark saulzar-bench
107116 loch-th,
108117 process
109118
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+
110125source-repository head
111126 type: git
112127 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 #-}
2−module Data.Functor.Misc where
3−
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'.
14+module Data.Functor.Misc
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
438import Data.GADT.Compare
39+import Data.GADT.Show
540import Data.Map (Map)
641import 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)
1042import Data.These
11−import Control.Monad.Identity
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
43+import Data.Typeable hiding (Refl)
1844
19−instance GCompare f => GCompare (WrapArg g f) where
20− gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
21− GLT -> GLT
22− GEQ -> GEQ
23− GGT -> GGT
45+--------------------------------------------------------------------------------
46+-- Const2
47+--------------------------------------------------------------------------------
2448
25−data Const2 :: * -> * -> * -> * where
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
51+data Const2 :: * -> x -> x -> * where
2652 Const2 :: k -> Const2 k v v
2753 deriving (Typeable)
2854
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+
2963instance Eq k => GEq (Const2 k v) where
3064 geq (Const2 a) (Const2 b) =
3165 if a == b
@@ -38,13 +72,57 @@ instance Ord k => GCompare (Const2 k v) where
3872 EQ -> GEQ
3973 GT -> GGT
4074
41−{-# INLINE sequenceDmap #-}
42−sequenceDmap :: (Monad m, GCompare f) => DMap f m -> m (DMap f Identity)
43−sequenceDmap = DMap.foldrWithKey (\k mv mx -> mx >>= \x -> mv >>= \v -> return $ DMap.insert k (Identity v) x)
44− (return DMap.empty)
75+-- | Convert a 'DMap' to a regular 'Map'
76+dmapToMap :: DMap (Const2 k v) Identity -> Map k v
77+dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
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
45116
46117{-# INLINE combineDMapsWithKey #-}
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
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.
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
48126combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
49127 where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
50128 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
54132 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
55133 GGT -> (hk :=> f hk (That hv)) : go gs hs'
56134
57−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
58−wrapDMap f = DMap.fromDistinctAscList . map (\(k :=> Identity v) -> k :=> f v) . DMap.toAscList
59−
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
135+--------------------------------------------------------------------------------
136+-- Deprecated functions
137+--------------------------------------------------------------------------------
65138
66−unwrapDMapMaybe :: (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
67−unwrapDMapMaybe f m = DMap.fromDistinctAscList [k :=> Identity w | (k :=> v) <- DMap.toAscList m, Just w <- [f v]]
68−
69−mapToDMap :: Map k v -> DMap (Const2 k v) Identity
70−mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
139+{-# INLINE sequenceDmap #-}
140+{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ t -> fmap Identity t)' instead" #-}
141+-- | Run the actions contained in the 'DMap'
142+sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
143+sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
71144
72−mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
73−mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> (Const2 k) :=> v) . Map.toAscList
145+{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
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
74149
75−dmapToMap :: DMap (Const2 k v) Identity -> Map k v
76−dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
150+{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
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−module Reflex ( module Reflex.Class
2− , module Reflex.Dynamic
3− , module Reflex.Dynamic.TH
4− , module Reflex.Spider
5− ) where
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.
3+module Reflex (module X) where
64
7−import Reflex.Class
8−import Reflex.Dynamic
9−import Reflex.Dynamic.TH
10−import Reflex.Spider
5+import Reflex.Class as X
6+import Reflex.Dynamic as X
7+import Reflex.Dynamic.Uniq as X
8+import Reflex.Dynamic.TH as X
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 #-}
2−module Reflex.Class where
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.
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
382
483import Control.Applicative
5−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
6−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
7−import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
8−import Control.Monad.Trans.Writer (WriterT())
9−import Control.Monad.Trans.Except (ExceptT())
10−import Control.Monad.Trans.Cont (ContT())
11−import Control.Monad.Trans.RWS (RWST())
12−import Data.List.NonEmpty (NonEmpty (..))
13−import Data.These
84+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
85+import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
86+import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
87+import Control.Monad.Trans.Cont (ContT ())
88+import Control.Monad.Trans.Except (ExceptT ())
89+import Control.Monad.Trans.RWS (RWST ())
90+import Control.Monad.Trans.Writer (WriterT ())
1491import Data.Align
15−import Data.GADT.Compare (GEq (..), (:~:) (..))
16−import Data.GADT.Show (GShow (..))
17−import Data.Dependent.Sum (ShowTag (..))
18−import Data.Map (Map)
92+import Data.Bifunctor
1993import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
2094import 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
21101import 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
23112import Data.Traversable
24113
25114-- 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_)
27116
28117import Debug.Trace (trace)
29118
30−class (MonadHold t (PushM t), MonadSample t (PullM t), MonadFix (PushM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
31− -- | A container for a value that can change over time. Behaviors can be sampled at will, but it is not possible to be notified when they change
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
134+ -- sampled at will, but it is not possible to be notified when they change
32135 data Behavior t :: * -> *
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")
136+ -- | A stream of occurrences. During any given frame, an 'Event' is either
137+ -- occurring or not occurring; if it is occurring, it will contain a value of
138+ -- the given type (its "occurrence type")
34139 data Event t :: * -> *
35− -- | An Event with no occurrences
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 :: (* -> *) -> * -> *
150+ -- | An 'Event' with no occurrences
36151 never :: Event t a
37− -- | Create a Behavior that always has the given value
38− constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of Behaviors doesn't have a negative performance impact
39− -- | Create an Event from another Event; the provided function can sample Behaviors and hold Events, and use the results to produce a occurring (Just) or non-occurring (Nothing) result
152+ -- | Create a 'Behavior' that always has the given value
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
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
40157 push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
41158 -- | A monad for doing complex push-based calculations efficiently
42159 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
44162 pull :: PullM t a -> Behavior t a
45163 -- | A monad for doing complex pull-based calculations efficiently
46164 type PullM t :: * -> *
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
165+ -- | Merge a collection of events; the resulting 'Event' will only occur if at
166+ -- least one input event is occuring, and will contain all of the input keys
167+ -- that are occurring simultaneously
48168 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
50172 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
52175 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
54178 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
55223
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'.
56250class (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'
58252 sample :: Behavior t a -> m a
59253
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.
60257class 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.
62264 hold :: a -> Event t a -> m (Behavior t a)
63−
64−newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
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'.
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+ }
65285
66286--------------------------------------------------------------------------------
67287-- Instances
@@ -72,85 +292,123 @@ instance MonadSample t m => MonadSample t (ReaderT r m) where
72292
73293instance MonadHold t m => MonadHold t (ReaderT r m) where
74294 hold a0 = lift . hold a0
295+ holdDyn a0 = lift . holdDyn a0
296+ holdIncremental a0 = lift . holdIncremental a0
75297
76298instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
77299 sample = lift . sample
78300
79301instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
80302 hold a0 = lift . hold a0
303+ holdDyn a0 = lift . holdDyn a0
304+ holdIncremental a0 = lift . holdIncremental a0
81305
82306instance MonadSample t m => MonadSample t (StateT s m) where
83307 sample = lift . sample
84308
85309instance MonadHold t m => MonadHold t (StateT s m) where
86310 hold a0 = lift . hold a0
311+ holdDyn a0 = lift . holdDyn a0
312+ holdIncremental a0 = lift . holdIncremental a0
87313
88314instance MonadSample t m => MonadSample t (ExceptT e m) where
89315 sample = lift . sample
90316
91317instance MonadHold t m => MonadHold t (ExceptT e m) where
92318 hold a0 = lift . hold a0
319+ holdDyn a0 = lift . holdDyn a0
320+ holdIncremental a0 = lift . holdIncremental a0
93321
94322instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
95323 sample = lift . sample
96324
97325instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
98326 hold a0 = lift . hold a0
327+ holdDyn a0 = lift . holdDyn a0
328+ holdIncremental a0 = lift . holdIncremental a0
99329
100330instance MonadSample t m => MonadSample t (ContT r m) where
101331 sample = lift . sample
102332
103333instance MonadHold t m => MonadHold t (ContT r m) where
104334 hold a0 = lift . hold a0
335+ holdDyn a0 = lift . holdDyn a0
336+ holdIncremental a0 = lift . holdIncremental a0
105337
106338--------------------------------------------------------------------------------
107339-- Convenience functions
108340--------------------------------------------------------------------------------
109341
110−-- | Create an Event from another Event.
111−-- The provided function can sample 'Behavior's and hold 'Event's.
342+-- | Create an 'Event' from another 'Event'. The provided function can sample
343+-- 'Behavior's and hold 'Event's.
112344pushAlways :: 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)
114346
115347-- | Flipped version of 'fmap'.
116348ffor :: Functor f => f a -> (a -> b) -> f b
117349ffor = flip fmap
118350
119−instance Reflex t => Functor (Behavior t) where
120− fmap f = pull . liftM f . sample
121−
122351instance Reflex t => Applicative (Behavior t) where
123352 pure = constant
124353 f <*> x = pull $ sample f `ap` sample x
125354 _ *> b = b
126355 a <* _ = a
127356
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+
128374instance Reflex t => Monad (Behavior t) where
129375 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.
131377 return = constant
132378 fail = error "Monad (Behavior t) does not support fail"
133379
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+
134394instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
135395 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
136− sconcat = pull . liftM sconcat . mapM sample
396+ sconcat = pull . fmap sconcat . mapM sample
137397#if MIN_VERSION_semigroups(0,17,0)
138398 stimes n = fmap $ stimes n
139399#else
140400 times1p n = fmap $ times1p n
141401#endif
142402
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−
148403--TODO: See if there's a better class in the standard libraries already
149404-- | A class for values that combines filtering and mapping using 'Maybe'.
150405class FunctorMaybe f where
151406 -- | Combined mapping and filtering function.
152407 fmapMaybe :: (a -> Maybe b) -> f a -> f b
153408
409+instance FunctorMaybe [] where
410+ fmapMaybe f = catMaybes . fmap f
411+
154412-- | Flipped version of 'fmapMaybe'.
155413fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
156414fforMaybe = flip fmapMaybe
@@ -160,53 +418,52 @@ fforMaybe = flip fmapMaybe
160418ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
161419ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
162420
163−instance Reflex t => FunctorMaybe (Event t) where
164− fmapMaybe f = push $ return . f
421+-- | Left-biased event union (prefers left event on simultaneous
422+-- occurrence).
423+instance Reflex t => Alt (Event t) where
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
165434
166435instance Reflex t => Functor (Event t) where
167436 fmap f = fmapMaybe $ Just . f
168437
169−-- | Create a new 'Event' by combining each occurence with the next value
170−-- of the list using the supplied function. If the list runs out of items,
171−-- all subsequent 'Event' occurrences will be ignored.
172−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
173−zipListWithEvent f l e = do
174− rec lb <- hold l eTail
175− let eBoth = flip push e $ \o -> do
176− l' <- sample lb
177− return $ case l' of
178− (h : t) -> Just (f h o, t)
179− [] -> Nothing
180− let eTail = fmap snd eBoth
181− lb `seq` eBoth `seq` eTail `seq` return ()
182− return $ fmap fst eBoth
438+instance Reflex t => FunctorMaybe (Event t) where
439+ fmapMaybe f = push $ return . f
440+
441+-- | Never: @'zero' = 'never'@.
442+instance Reflex t => Plus (Event t) where
443+ zero = never
183444
184445-- | Replace each occurrence value of the 'Event' with the value of the
185446-- 'Behavior' at the time of that occurrence.
186447tag :: Reflex t => Behavior t b -> Event t a -> Event t b
187448tag b = pushAlways $ \_ -> sample b
188449
189−-- | Create a new 'Event' that combines occurences of supplied 'Event'
190−-- with the current value of the 'Behavior'.
450+-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
451+-- current value of the 'Behavior'.
191452attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
192453attach = attachWith (,)
193454
194−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
195−-- by combining it with the current value of the 'Behavior'.
455+-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
456+-- combining it with the current value of the 'Behavior'.
196457attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
197458attachWith f = attachWithMaybe $ \a b -> Just $ f a b
198459
199−-- | Create a new 'Event' by combining each occurence with the current
200−-- value of the 'Behavior'. The occurrence is discarded if the combining function
201−-- returns Nothing
460+-- | Create a new 'Event' by combining each occurence with the current value of
461+-- the 'Behavior'. The occurrence is discarded if the combining function returns
462+-- Nothing
202463attachWithMaybe :: 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
204465
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
210467-- the supplied 'Event'.
211468headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
212469headE e = do
@@ -215,38 +472,38 @@ headE e = do
215472 e' `seq` return ()
216473 return e'
217474
218−-- | Create a new 'Event' that occurs on all but the first occurence
219−-- of the supplied 'Event'.
475+-- | Create a new 'Event' that occurs on all but the first occurence of the
476+-- supplied 'Event'.
220477tailE :: (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
222479
223−-- | Create a tuple of two 'Event's with the first one occuring only
224−-- the first time the supplied 'Event' occurs and the second occuring
225−-- on all but the first occurence.
480+-- | Create a tuple of two 'Event's with the first one occuring only the first
481+-- time the supplied 'Event' occurs and the second occuring on all but the first
482+-- occurence.
226483headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
227484headTailE e = do
228485 eHead <- headE e
229486 be <- hold never $ fmap (const e) eHead
230487 return (eHead, switch be)
231488
232−-- | Split the supplied 'Event' into two individual 'Event's occuring
233−-- at the same time with the respective values from the tuple.
489+-- | Split the supplied 'Event' into two individual 'Event's occuring at the
490+-- same time with the respective values from the tuple.
234491splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
235492splitE e = (fmap fst e, fmap snd e)
236493
237494-- | Print the supplied 'String' and the value of the 'Event' on each
238495-- occurence. This should /only/ be used for debugging.
239496--
240−-- Note: As with Debug.Trace.trace, the message will only be printed if
241−-- the 'Event' is actually used.
497+-- Note: As with Debug.Trace.trace, the message will only be printed if the
498+-- 'Event' is actually used.
242499traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
243500traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
244501
245−-- | Print the output of the supplied function on each occurence of
246−-- the 'Event'. This should /only/ be used for debugging.
502+-- | Print the output of the supplied function on each occurence of the
503+-- 'Event'. This should /only/ be used for debugging.
247504--
248−-- Note: As with Debug.Trace.trace, the message will only be printed if
249−-- the 'Event' is actually used.
505+-- Note: As with Debug.Trace.trace, the message will only be printed if the
506+-- 'Event' is actually used.
250507traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
251508traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
252509
@@ -298,59 +555,56 @@ dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
298555 (Nothing, Just (Identity b)) -> Just $ That b
299556 (Just (Identity a), Just (Identity b)) -> Just $ These a b
300557
301−-- | Create a new 'Event' that occurs if at least one of the supplied
302−-- 'Event's occurs. If both occur at the same time they are combined
303−-- using 'mappend'.
304−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
305−appendEvents e1 e2 = mergeThese mappend <$> align e1 e2
306−
307−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
308−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
309−sequenceThese t = case t of
310− This ma -> liftM This ma
311− These ma mb -> liftM2 These ma mb
312− That mb -> liftM That mb
558+instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
559+ (<>) = alignWith (mergeThese (<>))
560+ sconcat = fmap sconcat . mergeList . toList
561+#if MIN_VERSION_semigroups(0,17,0)
562+ stimes n = fmap $ stimes n
563+#else
564+ times1p n = fmap $ times1p n
565+#endif
313566
314567instance (Semigroup a, Reflex t) => Monoid (Event t a) where
315568 mempty = never
316− mappend a b = mconcat [a, b]
569+ mappend = (<>)
317570 mconcat = fmap sconcat . mergeList
318571
319−-- | Create a new 'Event' that occurs if at least one of the 'Event's
320−-- in the list occurs. If multiple occur at the same time they are
321−-- folded from the left with the given function.
572+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
573+-- list occurs. If multiple occur at the same time they are folded from the left
574+-- with the given function.
322575mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
323576mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
324577 . merge
325578 . DMap.fromDistinctAscList
326− . map (\(k, v) -> (Const2 k) :=> v)
579+ . map (\(k, v) -> Const2 k :=> v)
327580 $ zip [0 :: Int ..] es
328581
329−-- | Create a new 'Event' that occurs if at least one of the 'Event's
330−-- in the list occurs. If multiple occur at the same time the value is
331−-- the value of the leftmost event.
582+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
583+-- list occurs. If multiple occur at the same time the value is the value of the
584+-- leftmost event.
332585leftmost :: Reflex t => [Event t a] -> Event t a
333586leftmost = mergeWith const
334587
335−-- | Create a new 'Event' that occurs if at least one of the 'Event's
336−-- in the list occurs and has a list of the values of all 'Event's
337−-- occuring at that time.
588+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
589+-- list occurs and has a list of the values of all 'Event's occuring at that
590+-- time.
338591mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
339592mergeList [] = never
340593mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
341594
342−-- | Create a new 'Event' combining the map of 'Event's into an
343−-- 'Event' that occurs if at least one of them occurs and has a map of
344−-- values of all 'Event's occuring at that time.
595+-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
596+-- occurs if at least one of them occurs and has a map of values of all 'Event's
597+-- occuring at that time.
345598mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
346599mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
347600
348−-- | Split the event into an 'EventSelector' that allows efficient
349−-- selection of the individual 'Event's.
601+-- | Split the event into an 'EventSelector' that allows efficient selection of
602+-- the individual 'Event's.
350603fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
351604fanMap = fan . fmap mapToDMap
352605
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
354608switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
355609switchPromptly ea0 eea = do
356610 bea <- hold ea0 eea
@@ -362,13 +616,164 @@ instance Reflex t => Align (Event t) where
362616 nil = never
363617 align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
364618
365−-- | Create a new 'Event' that only occurs if the supplied 'Event'
366−-- occurs and the 'Behavior' is true at the time of occurence.
619+-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
620+-- the 'Behavior' is true at the time of occurence.
367621gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
368622gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
369623
370−-- | Create a new behavior given a starting behavior and switch to a the
371−-- behvior carried by the event when it fires.
624+-- | Create a new behavior given a starting behavior and switch to a the behvior
625+-- carried by the event when it fires.
372626switcher :: (Reflex t, MonadHold t m)
373627 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
374628switcher 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 #-}
2−module Reflex.Dynamic ( Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
3− , current
4− , updated
5− , constDyn
6− , holdDyn
7− , nubDyn
8− , count
9− , toggle
10− , switchPromptlyDyn
11− , tagDyn
12− , attachDyn
13− , attachDynWith
14− , attachDynWithMaybe
15− , mapDyn
16− , forDyn
17− , mapDynM
18− , foldDyn
19− , foldDynM
20− , foldDynMaybe
21− , foldDynMaybeM
22− , combineDyn
23− , apDyn
24− , collectDyn
25− , mconcatDyn
26− , distributeDMapOverDyn
27− , joinDyn
28− , joinDynThroughMap
29− , traceDyn
30− , traceDynWith
31− , splitDyn
32− , Demux
33− , demux
34− , getDemuxed
35− -- Things that probably aren't very useful:
36− , HList (..)
37− , FHList (..)
38− , distributeFHListOverDyn
39− -- Unsafe
40− , unsafeDynamic
41− ) where
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
19+ Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
20+ , current
21+ , updated
22+ , holdDyn
23+ , constDyn
24+ , uniqDyn
25+ , uniqDynBy
26+ , count
27+ , toggle
28+ , switchPromptlyDyn
29+ , tagPromptlyDyn
30+ , attachPromptlyDyn
31+ , attachPromptlyDynWith
32+ , attachPromptlyDynWithMaybe
33+ , foldDyn
34+ , foldDynM
35+ , foldDynMaybe
36+ , foldDynMaybeM
37+ , joinDynThroughMap
38+ , traceDyn
39+ , traceDynWith
40+ , splitDynPure
41+ , distributeDMapOverDynPure
42+ , Demux
43+ , demux
44+ , demuxed
45+ -- Things that probably aren't very useful:
46+ , HList (..)
47+ , FHList (..)
48+ , collectDynPure
49+ , distributeFHListOverDynPure
50+ -- Unsafe
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
69+ ) where
4270
4371import Prelude hiding (mapM, mapM_)
4472
45−import Reflex.Class
4673import Data.Functor.Misc
74+import Reflex.Class
4775
48−import Control.Monad hiding (mapM, mapM_, forM, forM_)
76+import Control.Applicative ((<*>))
77+import Control.Monad hiding (forM, forM_, mapM, mapM_)
4978import Control.Monad.Fix
50−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_)
51−import Data.These
52−import Data.Traversable (mapM, forM)
79+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
5380import Data.Align
54−import Data.Map (Map)
55−import qualified Data.Map as Map
5681import Data.Dependent.Map (DMap)
5782import qualified Data.Dependent.Map as DMap
5883import 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)
6086import Data.Monoid
61−--import Data.HList (HList (..), hBuild)
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
87+import Data.These
8988
90−instance HBuild' (a ': l) r
91− => HBuild' l (a->r) where
92− hBuild' l x = hBuild' (HCons x l)
89+import Debug.Trace
9390
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
128−-- actually changed.
129−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
130−nubDyn d =
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−-}
91+-- | Create a new 'Dynamic' that only signals changes if the values actually
92+-- changed.
93+uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
94+uniqDyn = uniqDynBy (==)
14295
143−-- | Map a function over a 'Dynamic'.
144−mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
145−mapDyn f = mapDynM $ return . f
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.
99+uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
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'
146103
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
164−-- time the 'Event' occurs using a folding function on the previous
165−-- value and the value of the 'Event'.
104+-- | Create a 'Dynamic' using the initial value and change it each time the
105+-- 'Event' occurs using a folding function on the previous value and the value
106+-- of the 'Event'.
166107foldDyn :: (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
168109
169−-- | Create a 'Dynamic' using the initial value and change it each
170−-- time the 'Event' occurs using a monadic folding function on the
171−-- previous value and the value of the 'Event'.
110+-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
111+-- can 'sample' existing 'Behaviors' and 'hold' new ones.
172112foldDynM :: (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
174−
113+foldDynM = accumM . flip
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.
175121foldDynMaybe :: (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
177123
124+-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
125+-- can 'sample' existing 'Behaviors' and 'hold' new ones.
178126foldDynMaybeM :: (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
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'
127+foldDynMaybeM = accumMaybeM . flip
185128
186129-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
187130count :: (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
192135toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
193136toggle = foldDyn (const not)
194137
195−-- | Switches to the new 'Event' whenever it receives one. Switching
196−-- occurs *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the old and new
197−-- inner Events fire simultaneously, the output will fire with the value of the *new* 'Event'.
138+-- | Switches to the new 'Event' whenever it receives one. Switching occurs
139+-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
140+-- old and new inner Events fire simultaneously, the output will fire with the
141+-- value of the *new* 'Event'.
198142switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
199143switchPromptlyDyn de =
200144 let eLag = switch $ current de
201145 eCoincidences = coincidence $ updated de
202146 in leftmost [eCoincidences, eLag]
203147
204−{-
205−
206−mergeEventsWith :: Reflex t m => (a -> a -> a) -> Event t a -> Event t a -> m (Event t a)
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−-}
148+-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
149+splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
150+splitDynPure d = (fmap fst d, fmap snd d)
222151
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
235−-- Dynamic values.
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.
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:
152+-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of 'Dynamic'
153+-- values. The resulting 'Dynamic' will change whenever one of the input
154+-- 'Dynamic's changes.
276155--
277−-- > do
278−-- > person <- Person `mapDyn` dynFirstName
279−-- > `apDyn` dynListName
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
301−joinDyn dd =
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'
156+-- This function's implementation is more efficient than simply using
157+-- 'zipDynWith' or applicative syntax to achieve the same result, especially for
158+-- maps with many elements.
159+distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
160+distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
308161
309162--TODO: Generalize this to functors other than Maps
310163-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
311164-- with the current values of the 'Dynamic's in a map.
312165joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
313−joinDynThroughMap dd =
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'
166+joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
325167
326168-- | Print the value of the 'Dynamic' on each change and prefix it
327169-- with the provided string. This should /only/ be used for debugging.
@@ -340,42 +182,46 @@ traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
340182traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
341183traceDynWith f d =
342184 let e' = traceEventWith f $ updated d
343− in Dynamic (current d) e'
185+ getV0 = do
186+ x <- sample $ current d
187+ trace (f x) $ return x
188+ in unsafeBuildDynamic getV0 e'
344189
345190-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
346191-- each time the 'Event' occurs.
347192--
348−-- Note: `tagDyn 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`
193+-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
194+-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
350195-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
351196-- will be used, since the 'Behavior' won't be updated until the end of the frame.
352197-- Additionally, this means that the output 'Event' may not be used to directly change
353198-- the input 'Dynamic', because that would mean its value depends on itself. When creating
354199-- cyclic data flows, generally `tag (current d) e` is preferred.
355−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
356−tagDyn = attachDynWith const
200+tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
201+tagPromptlyDyn = attachPromptlyDynWith const
357202
358203-- | Attach the current value of the 'Dynamic' to the value of the
359204-- 'Event' each time it occurs.
360205--
361−-- Note: `attachDyn d` is not the same as `attach (current d)`. See 'tagDyn' for details.
362−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
363−attachDyn = attachDynWith (,)
206+-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
207+attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
208+attachPromptlyDyn = attachPromptlyDynWith (,)
364209
365210-- | Combine the current value of the 'Dynamic' with the value of the
366211-- 'Event' each time it occurs.
367212--
368−-- Note: `attachDynWith f d` is not the same as `attachWith f (current d)`. See 'tagDyn' for details.
369−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
370−attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
213+-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
214+attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
215+attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
371216
372−-- | Create a new 'Event' by combining the value at each occurence
373−-- with the current value of the 'Dynamic' value and possibly
374−-- filtering if the combining function returns 'Nothing'.
217+-- | Create a new 'Event' by combining the value at each occurence with the
218+-- current value of the 'Dynamic' value and possibly filtering if the combining
219+-- function returns 'Nothing'.
375220--
376−-- Note: `attachDynWithMaybe f d` is not the same as `attachWithMaybe f (current d)`. See 'tagDyn' for details.
377−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
378−attachDynWithMaybe f d e =
221+-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
222+-- (current d)`. See 'tagPromptlyDyn' for details.
223+attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
224+attachPromptlyDynWithMaybe f d e =
379225 let e' = attach (current d) e
380226 in fforMaybe (align e' $ updated d) $ \case
381227 This (a, b) -> f a b -- Only the tagging event is firing, so use that
@@ -386,36 +232,38 @@ attachDynWithMaybe f d e =
386232-- Demux
387233--------------------------------------------------------------------------------
388234
389−-- | Represents a time changing value together with an 'EventSelector'
390−-- that can 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.
235+-- | Represents a time changing value together with an 'EventSelector' that can
236+-- efficiently detect when the underlying 'Dynamic' has a particular value.
237+-- This is useful for representing data like the current selection of a long
238+-- list.
392239--
393240-- Semantically,
394−-- > getDemuxed (demux d) k === mapDyn (== k) d
395−-- However, the when getDemuxed is used multiple times, the complexity is only /O(log(n))/,
396−-- rather than /O(n)/ for mapDyn.
241+--
242+-- > demuxed (demux d) k === fmap (== k) d
243+--
244+-- However, the when getDemuxed is used multiple times, the complexity is only
245+-- /O(log(n))/, rather than /O(n)/ for fmap.
397246data Demux t k = Demux { demuxValue :: Behavior t k
398247 , demuxSelector :: EventSelector t (Const2 k Bool)
399248 }
400249
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'.
250+-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
251+-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
402252demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
403253demux k = Demux (current k)
404− (fan $ attachWith (\k0 k1 -> if k0 == k1
254+ (fan $ attachWith (\k0 k1 -> if k0 == k1
405255 then DMap.empty
406256 else DMap.fromList [Const2 k0 :=> Identity False,
407257 Const2 k1 :=> Identity True])
408258 (current k) (updated k))
409259
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)
412−-- mapping over the original 'Dynamic' and checking whether it is equal to the given key.
413−getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
414−getDemuxed d k = do
260+-- | Select a particular output of the 'Demux'; this is equivalent to (but much
261+-- faster than) mapping over the original 'Dynamic' and checking whether it is
262+-- equal to the given key.
263+demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
264+demuxed d k =
415265 let e = select (demuxSelector d) (Const2 k)
416− bb <- hold (liftM (==k) $ sample $ demuxValue d) $ fmap return e
417− let b = pull $ join $ sample bb
418− return $ Dynamic b e
266+ in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
419267
420268--------------------------------------------------------------------------------
421269-- collectDyn
@@ -423,6 +271,41 @@ getDemuxed d k = do
423271
424272--TODO: This whole section is badly in need of cleanup
425273
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.
426309data FHList f l where
427310 FHNil :: FHList f '[]
428311 FHCons :: f e -> FHList f l -> FHList f (e ': l)
@@ -448,7 +331,7 @@ fhlistToDMap = DMap.fromList . go
448331 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
449332 go = \case
450333 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)
452335
453336class RebuildSortedHList l where
454337 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
@@ -473,16 +356,11 @@ instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
473356dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
474357dmapToHList = rebuildSortedHList . DMap.toList
475358
476−distributeFHListOverDyn :: forall t m l. (Reflex t, MonadHold t m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
477−distributeFHListOverDyn l = mapDyn dmapToHList =<< distributeDMapOverDyn (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−-}
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.
362+distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
363+distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
486364
487365class AllAreFunctors (f :: a -> *) (l :: [a]) where
488366 type FunctorList f l :: [*]
@@ -493,24 +371,30 @@ instance AllAreFunctors f '[] where
493371 type FunctorList f '[] = '[]
494372 toFHList l = case l of
495373 HNil -> FHNil
374+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
496375 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
376+#endif
497377 fromFHList FHNil = HNil
498378
499379instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
500380 type FunctorList f (h ': t) = f h ': FunctorList f t
501381 toFHList l = case l of
502382 a `HCons` b -> a `FHCons` toFHList b
383+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
503384 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
385+#endif
504386 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
505387
506−collectDyn :: ( RebuildSortedHList (HListElems b)
507− , IsHList a, IsHList b
508− , AllAreFunctors (Dynamic t) (HListElems b)
509− , Reflex t, MonadHold t m
510− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
511− ) => a -> m (Dynamic t b)
512−collectDyn ds =
513− mapDyn fromHList =<< distributeFHListOverDyn (toFHList $ toHList ds)
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.
391+collectDynPure :: ( RebuildSortedHList (HListElems b)
392+ , IsHList a, IsHList b
393+ , AllAreFunctors (Dynamic t) (HListElems b)
394+ , Reflex t
395+ , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
396+ ) => a -> Dynamic t b
397+collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
514398
515399-- Poor man's Generic
516400class IsHList a where
@@ -523,18 +407,137 @@ instance IsHList (a, b) where
523407 toHList (a, b) = hBuild a b
524408 fromHList l = case l of
525409 a `HCons` b `HCons` HNil -> (a, b)
410+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
526411 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
412+#endif
527413
528414instance IsHList (a, b, c, d) where
529415 type HListElems (a, b, c, d) = [a, b, c, d]
530416 toHList (a, b, c, d) = hBuild a b c d
531417 fromHList l = case l of
532418 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
419+#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
533420 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
421+#endif
534422
535423instance IsHList (a, b, c, d, e, f) where
536424 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
537425 toHList (a, b, c, d, e, f) = hBuild a b c d e f
538426 fromHList l = case l of
539427 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
540429 _ -> 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.
28module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
39
410import Reflex.Dynamic
511
6−import Language.Haskell.TH
7−import qualified Language.Haskell.TH.Syntax as TH
8−import Language.Haskell.TH.Quote
9−import Data.Data
1012import Control.Monad.State
13+import Data.Data
14+import Data.Generics
15+import Data.Monoid
1116import qualified Language.Haskell.Exts as Hs
1217import qualified Language.Haskell.Meta.Syntax.Translate as Hs
13−import Data.Monoid
14−import Data.Generics
18+import Language.Haskell.TH
19+import Language.Haskell.TH.Quote
20+import qualified Language.Haskell.TH.Syntax as TH
1521
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@
1725qDyn :: Q Exp -> Q Exp
1826qDyn qe = do
1927 e <- qe
@@ -30,19 +38,32 @@ qDyn qe = do
3038 let exprs = reverse exprsReversed
3139 arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
3240 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) |]
3442
43+-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
44+-- 'qDyn' quotation.
3545unqDyn :: Q Exp -> Q Exp
3646unqDyn e = [| unqMarker $e |]
3747
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.
3951data UnqDyn
4052
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+
4256--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
4357unqMarker :: a -> UnqDyn
4458unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
4559
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.
4667mkDyn :: QuasiQuoter
4768mkDyn = QuasiQuoter
4869 { 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 #-}
2−module Reflex.Host.Class where
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.
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
326
427import Reflex.Class
528
@@ -7,49 +30,63 @@ import Control.Applicative
730import Control.Monad
831import Control.Monad.Fix
932import Control.Monad.Identity
33+import Control.Monad.Ref
1034import Control.Monad.Trans
11−import Control.Monad.Trans.Reader (ReaderT())
12−import Control.Monad.Trans.Writer (WriterT())
13−import Control.Monad.Trans.Cont (ContT())
14−import Control.Monad.Trans.Except (ExceptT())
15−import Control.Monad.Trans.RWS (RWST())
16−import Control.Monad.Trans.State (StateT())
35+import Control.Monad.Trans.Cont (ContT ())
36+import Control.Monad.Trans.Except (ExceptT ())
37+import Control.Monad.Trans.Reader (ReaderT ())
38+import Control.Monad.Trans.RWS (RWST ())
39+import Control.Monad.Trans.State (StateT ())
1740import qualified Control.Monad.Trans.State.Strict as Strict
41+import Control.Monad.Trans.Writer (WriterT ())
1842import Data.Dependent.Sum (DSum (..))
19−import Data.Monoid
2043import Data.GADT.Compare
21−import Control.Monad.Ref
44+import Data.Monoid
2245
2346-- Note: this import must come last to silence warnings from AMP
24−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
25−
26−-- | Framework implementation support class for the reflex implementation represented by @t@.
27−class (Reflex t, MonadReflexCreateTrigger t (HostFrame t), MonadSample t (HostFrame t), MonadHold t (HostFrame t), MonadFix (HostFrame t), MonadSubscribeEvent t (HostFrame t)) => ReflexHost t where
47+import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
48+
49+-- | Framework implementation support class for the reflex implementation
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
2858 type EventTrigger t :: * -> *
2959 type EventHandle t :: * -> *
3060 type HostFrame t :: * -> *
3161
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
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
64+-- ought to. Events must be subscribed before they are read using readEvent
3365class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
3466 -- | Subscribe to an event and set it up if needed.
3567 --
36− -- This function will create a new 'EventHandle' from an 'Event'. This handle may then be used via
37− -- 'readEvent' in the read callback of 'fireEventsAndRead'.
68+ -- This function will create a new 'EventHandle' from an 'Event'. This handle
69+ -- may then be used via 'readEvent' in the read callback of
70+ -- 'fireEventsAndRead'.
3871 --
39− -- If the event wasn't subscribed to before (either manually or through a dependent event or behavior)
40− -- then this function will cause the event and all dependencies of this event to be set up.
41− -- For example, if the event was created by 'newEventWithTrigger', then it's callback will be executed.
72+ -- If the event wasn't subscribed to before (either manually or through a
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
75+ -- created by 'newEventWithTrigger', then it's callback will be executed.
4276 --
4377 -- It's safe to call this function multiple times.
4478 subscribeEvent :: Event t a -> m (EventHandle t a)
4579
4680-- | Monad that allows to read events' values.
4781class (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').
4984 --
50− -- After event propagation is done, all events can be in two states: either they are firing with some value or they are not firing.
51− -- In the former case, this function returns @Just act@, where @act@ in an action to read the current value of
52− -- the event. In the latter case, the function returns @Nothing@.
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
87+ -- case, this function returns @Just act@, where @act@ in an action to read
88+ -- the current value of the event. In the latter case, the function returns
89+ -- @Nothing@.
5390 --
5491 -- This function is normally used in the calllback for 'fireEventsAndRead'.
5592 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
5895class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
5996 -- | Creates a root 'Event' (one that is not based on any other event).
6097 --
61− -- When a subscriber first subscribes to an event (building another event
62− -- that depends on the subscription) the given callback function is run and
63− -- passed a trigger. The callback function can then set up the event source in IO.
64− -- After this is done, the callback function must return an accompanying teardown action.
98+ -- When a subscriber first subscribes to an event (building another event that
99+ -- depends on the subscription) the given callback function is run and passed
100+ -- a trigger. The callback function can then set up the event source in IO.
101+ -- After this is done, the callback function must return an accompanying
102+ -- teardown action.
65103 --
66− -- Any time between setup and teardown the trigger can be used to fire
67− -- the event, by passing it to 'fireEventsAndRead'.
104+ -- Any time between setup and teardown the trigger can be used to fire the
105+ -- event, by passing it to 'fireEventsAndRead'.
68106 --
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.
70109 newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
71110 newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
72111
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
74120 type ReadPhase m :: * -> *
75121 -- | Propagate some events firings and read the values of events afterwards.
76122 --
77− -- This function will create a new frame to fire the given events. It will then update all
78− -- dependent events and behaviors. After that is done, the given callback is executed which
79− -- allows to read the final values of events and check whether they have fired in this frame or
80− -- not.
123+ -- This function will create a new frame to fire the given events. It will
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
126+ -- and check whether they have fired in this frame or not.
81127 --
82128 -- All events that are given are fired at the same time.
83129 --
84− -- This function is typically used in the main loop of a reflex framework implementation.
85− -- The main loop waits for external events to happen (such as keyboard input or a mouse click)
86− -- and then fires the corresponding events using this function. The read callback can be used
87− -- to read output events and perform a corresponding response action to the external event.
88− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> (ReadPhase m a) -> m a
130+ -- This function is typically used in the main loop of a reflex framework
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
133+ -- using this function. The read callback can be used to read output events
134+ -- and perform a corresponding response action to the external event.
135+ fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
89136
90137 -- | Run a frame without any events firing.
91138 --
92− -- This function should be used when you want to use 'sample' and 'hold' when no events are currently firing.
93− -- Using this function in that case can improve performance, since the implementation can assume that no events
94− -- are firing when 'sample' or 'hold' are called.
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
141+ -- improve performance, since the implementation can assume that no events are
142+ -- firing when 'sample' or 'hold' are called.
95143 --
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.
97146 runHostFrame :: HostFrame t a -> m a
98147
99148-- | Like 'fireEventsAndRead', but without reading any events.
@@ -103,10 +152,13 @@ fireEvents dm = fireEventsAndRead dm $ return ()
103152
104153-- | Create a new event and store its trigger in an 'IORef' while it's active.
105154--
106−-- An event is only active between the set up (when it's first subscribed to) and the teardown phases (when noboby is subscribing the event anymore).
107−-- This function returns an Event and an 'IORef'. As long as the event is active, the 'IORef' will contain 'Just' the event trigger to
108−-- trigger this event. When the event is not active, the 'IORef' will contain 'Nothing'. This allows event sources to be more efficient,
109−-- since they don't need to produce events when nobody is listening.
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
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
159+-- event is not active, the 'IORef' will contain 'Nothing'. This allows event
160+-- sources to be more efficient, since they don't need to produce events when
161+-- nobody is listening.
110162newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
111163newEventWithTriggerRef = do
112164 rt <- newRef Nothing
@@ -116,6 +168,7 @@ newEventWithTriggerRef = do
116168 return (e, rt)
117169{-# INLINE newEventWithTriggerRef #-}
118170
171+-- | Fire the given trigger if it is not 'Nothing'.
119172fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
120173fireEventRef mtRef input = do
121174 mt <- readRef mtRef
@@ -123,6 +176,8 @@ fireEventRef mtRef input = do
123176 Nothing -> return ()
124177 Just trigger -> fireEvents [trigger :=> Identity input]
125178
179+-- | Fire the given trigger if it is not 'Nothing', and read from the given
180+-- 'EventHandle'.
126181fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
127182fireEventRefAndRead mtRef input e = do
128183 mt <- readRef mtRef
@@ -132,7 +187,7 @@ fireEventRefAndRead mtRef input e = do
132187 mGetValue <- readEvent e
133188 case mGetValue of
134189 Nothing -> return Nothing
135− Just getValue -> liftM Just getValue
190+ Just getValue -> fmap Just getValue
136191
137192
138193--------------------------------------------------------------------------------
@@ -174,8 +229,8 @@ instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
174229 type ReadPhase (StateT s m) = ReadPhase m
175230 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
176231 runHostFrame = lift . runHostFrame
177−
178−
232+
233+
179234instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
180235 newEventWithTrigger = lift . newEventWithTrigger
181236 newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
@@ -186,9 +241,9 @@ instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) wh
186241instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
187242 type ReadPhase (Strict.StateT s m) = ReadPhase m
188243 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
189− runHostFrame = lift . runHostFrame
190−
191−
244+ runHostFrame = lift . runHostFrame
245+
246+
192247
193248instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
194249 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
13module Reflex.Spider ( Spider
2− , SpiderHost (..) --Temporary, until Widget no longer uses unsafePerformIO
4+ , SpiderEnv
5+ , Global
6+ , SpiderHost
7+ , runSpiderHost
38 ) where
49
510import 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 #-}
2−module Reflex.Spider.Internal where
3−
4−import qualified Reflex.Class as R
5−import qualified Reflex.Host.Class as R
6−
7−import Data.IORef
8−import System.Mem.Weak
9−import Data.Foldable
10−import Data.Traversable
11−import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
12−import Control.Monad.Identity hiding (mapM, mapM_, forM_, forM, sequence)
13−import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
14−import GHC.Exts
15−import Control.Applicative -- Unconditionally import, because otherwise it breaks on GHC 7.10.1RC2
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.
23+module Reflex.Spider.Internal
24+ ( Spider
25+ , SpiderEnv (..)
26+ , Global
27+ , SpiderHost
28+ , runSpiderHost
29+ , newSpiderEnv
30+ , askSpiderEnv
31+ ) where
32+
33+import Control.Applicative
34+import Control.Concurrent
35+import Control.Exception
36+import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
37+import Control.Monad.Exception
38+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
39+import Control.Monad.Primitive
40+import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
41+import Control.Monad.Ref
42+import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
43+import Data.Align
44+import Data.Coerce
1645import Data.Dependent.Map (DMap, DSum (..))
1746import qualified Data.Dependent.Map as DMap
47+import Data.Foldable hiding (concat, elem, sequence_)
48+import Data.Function
49+import Data.Functor.Compose
1850import 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
1957import Data.Maybe
20−import Data.IntMap (IntMap)
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
26−import Control.Exception
27−
58+import Data.Monoid (mempty, (<>))
59+import Data.Some (Some)
60+import qualified Data.Some as Some
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
2867import System.IO.Unsafe
68+import System.Mem.Weak
2969import 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
3175
3276-- 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)
3481
3582debugPropagate :: Bool
3683
3784debugInvalidateHeight :: Bool
3885
86+debugInvalidate :: Bool
87+
3988#ifdef DEBUG
4089
4190#define DEBUG_NODEIDS
4291
4392debugPropagate = True
4493
45−debugInvalidateHeight = True
94+debugInvalidateHeight = False
95+
96+debugInvalidate = True
4697
4798class HasNodeId a where
4899 getNodeId :: a -> Int
49100
50−instance HasNodeId (Hold a) where
101+instance HasNodeId (Hold p a) where
51102 getNodeId = holdNodeId
52103
53104instance HasNodeId (PushSubscribed a b) where
@@ -68,17 +119,52 @@ instance HasNodeId (CoincidenceSubscribed a) where
68119instance HasNodeId (RootSubscribed a) where
69120 getNodeId = rootSubscribedNodeId
70121
122+instance HasNodeId (Pull a) where
123+ getNodeId = pullNodeId
124+
125+{-# INLINE showNodeId #-}
71126showNodeId :: HasNodeId a => a -> String
72127showNodeId = ("#"<>) . show . getNodeId
73128
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+
74156#else
75157
76158debugPropagate = False
77159
78160debugInvalidateHeight = False
79161
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 #-}
80166showNodeId :: a -> String
81−showNodeId = const ""
167+showNodeId _ = ""
82168
83169#endif
84170
@@ -87,116 +173,189 @@ showNodeId = const ""
87173nextNodeIdRef :: IORef Int
88174nextNodeIdRef = unsafePerformIO $ newIORef 1
89175
176+newNodeId :: IO Int
177+newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
178+
90179{-# NOINLINE unsafeNodeId #-}
91180unsafeNodeId :: a -> Int
92181unsafeNodeId a = unsafePerformIO $ do
93182 touch a
94− atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
183+ newNodeId
95184#endif
96185
97186--TODO: Figure out why certain things are not 'representational', then make them representational so we can use coerce
98187--type role Hold representational
99−data Hold a
188+data Hold p a
100189 = Hold { holdValue :: !(IORef a)
101190 , 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
105192#ifdef DEBUG_NODEIDS
106193 , holdNodeId :: Int
107194#endif
108195 }
109196
110−data EventEnv
111− = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment])
112− , eventEnvHoldInits :: !(IORef [SomeHoldInit])
113− , eventEnvClears :: !(IORef [SomeMaybeIORef])
114− , eventEnvRootClears :: !(IORef [SomeDMapIORef])
115− , eventEnvCurrentHeight :: !(IORef Int)
116− , eventEnvCoincidenceInfos :: !(IORef [SomeCoincidenceInfo])
197+data EventEnv x
198+ = EventEnv { eventEnvSpiderEnv :: !(SpiderEnv x)
199+ , eventEnvAssignments :: !(IORef [SomeAssignment]) -- Needed for Subscribe
200+ , eventEnvHoldInits :: !(IORef [SomeHoldInit]) -- Needed for Subscribe
201+ , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate])
202+ , eventEnvMergeInits :: !(IORef [SomeMergeInit]) -- Needed for Subscribe
203+ , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
204+ , eventEnvRootClears :: !(IORef [SomeRootClear])
205+ , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
206+ , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence]) -- Needed for Subscribe
117207 , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
118208 }
119209
120−runEventM :: EventM a -> EventEnv -> IO a
210+{-# INLINE runEventM #-}
211+runEventM :: EventM x a -> EventEnv x -> IO a
121212runEventM = runReaderT . unEventM
122213
123−askToAssignRef :: EventM (IORef [SomeAssignment])
124−askToAssignRef = EventM $ asks eventEnvAssignments
125−
126−askHoldInitRef :: EventM (IORef [SomeHoldInit])
127−askHoldInitRef = EventM $ asks eventEnvHoldInits
128−
129−getCurrentHeight :: EventM Int
130−getCurrentHeight = EventM $ do
131− heightRef <- asks eventEnvCurrentHeight
132− liftIO $ readIORef heightRef
133−
134−putCurrentHeight :: Int -> EventM ()
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 #-}
225+ getDeferralQueue = EventM $ asks eventEnvAssignments
226+
227+instance Defer SomeHoldInit (EventM x) where
228+ {-# INLINE getDeferralQueue #-}
229+ getDeferralQueue = EventM $ asks eventEnvHoldInits
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
244+ getCurrentHeight :: m Height
245+ scheduleMerge :: Height -> MergeSubscribed a -> m ()
246+
247+instance HasCurrentHeight (EventM x) where
248+ {-# INLINE getCurrentHeight #-}
249+ getCurrentHeight = EventM $ do
250+ heightRef <- asks eventEnvCurrentHeight
251+ liftIO $ readIORef heightRef
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+
269+putCurrentHeight :: Height -> EventM x ()
135270putCurrentHeight h = EventM $ do
136271 heightRef <- asks eventEnvCurrentHeight
137− liftIO $ writeIORef heightRef h
272+ liftIO $ writeIORef heightRef $! h
138273
139−scheduleClear :: IORef (Maybe a) -> EventM ()
140−scheduleClear r = EventM $ do
141− clears <- asks eventEnvClears
142− liftIO $ modifyIORef' clears (SomeMaybeIORef r :)
274+instance Defer SomeClear (EventM x) where
275+ {-# INLINE getDeferralQueue #-}
276+ getDeferralQueue = EventM $ asks eventEnvClears
143277
144−scheduleRootClear :: IORef (DMap k Identity) -> EventM ()
145−scheduleRootClear r = EventM $ do
146− clears <- asks eventEnvRootClears
147− liftIO $ modifyIORef' clears (SomeDMapIORef r :)
278+{-# INLINE scheduleClear #-}
279+scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
280+scheduleClear r = defer $ SomeClear r
148281
149−scheduleMerge :: Int -> MergeSubscribed a -> EventM ()
150−scheduleMerge height subscribed = EventM $ do
151− delayedRef <- asks eventEnvDelayedMerges
152− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) height [DelayedMerge subscribed]
282+instance Defer SomeRootClear (EventM x) where
283+ {-# INLINE getDeferralQueue #-}
284+ getDeferralQueue = EventM $ asks eventEnvRootClears
153285
154−emitCoincidenceInfo :: SomeCoincidenceInfo -> EventM ()
155−emitCoincidenceInfo sci = EventM $ do
156− ciRef <- asks eventEnvCoincidenceInfos
157− liftIO $ modifyIORef' ciRef (sci:)
286+{-# INLINE scheduleRootClear #-}
287+scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
288+scheduleRootClear r = defer $ SomeRootClear r
289+
290+instance Defer SomeResetCoincidence (EventM x) where
291+ {-# INLINE getDeferralQueue #-}
292+ getDeferralQueue = EventM $ asks eventEnvResetCoincidences
158293
159294-- 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)
161298hold v0 e = do
162− holdInitRef <- askHoldInitRef
163− liftIO $ do
164− valRef <- newIORef v0
165− invsRef <- newIORef []
166− parentRef <- newIORef $ error "hold not yet initialized (parent)"
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
299+ valRef <- liftIO $ newIORef v0
300+ invsRef <- liftIO $ newIORef []
301+ parentRef <- liftIO $ newIORef $ Left e
173302#ifdef DEBUG_NODEIDS
174− , holdNodeId = unsafeNodeId (v0, e)
303+ nodeId <- liftIO newNodeId
175304#endif
176− }
177− s <- newSubscriberHold h
178− writeIORef subscriberRef $ unsafeCoerce s
179− modifyIORef' holdInitRef (SomeHoldInit e h :)
180− return $ BehaviorHold h
181−
182−subscribeHold :: Event a -> Hold a -> EventM ()
183−subscribeHold e h = do
184− toAssignRef <- askToAssignRef
185− !s <- liftIO $ liftM unsafeCoerce $ readIORef $ holdSubscriber h -- This must be performed strictly so that the weak pointer points at the actual item
186− ws <- liftIO $ mkWeakPtrWithDebug s "holdSubscriber"
187− subd <- subscribe e $ WeakSubscriberSimple ws
188− liftIO $ writeIORef (holdParent h) $ unsafeCoerce subd
189− occ <- liftIO $ getEventSubscribedOcc subd
190− case occ of
191− Nothing -> return ()
192− Just o -> liftIO $ modifyIORef' toAssignRef (SomeAssignment h o :)
305+ let h = Hold
306+ { holdValue = valRef
307+ , holdInvalidators = invsRef
308+ , holdParent = parentRef
309+#ifdef DEBUG_NODEIDS
310+ , holdNodeId = nodeId
311+#endif
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"
324+ subscription@(EventSubscription _ subd) <- subscribe e =<< liftIO (newSubscriberHold h)
325+ liftIO $ writeIORef subscriptionRef $! subscription
326+ occ <- liftIO $ readEventSubscribed subd
327+ case occ of
328+ Nothing -> return ()
329+ Just o -> do
330+ old <- liftIO $ readIORef $ holdValue h
331+ case R.apply o old of
332+ Nothing -> return ()
333+ Just new -> do
334+ -- Need to evaluate these so that we don't retain the Hold itself
335+ v <- liftIO $ evaluate $ holdValue h
336+ i <- liftIO $ evaluate $ holdInvalidators h
337+ defer $ SomeAssignment v i new
338+ liftIO $ writeIORef (holdParent h) $ Right subscription
339+ return subscription
340+
341+type BehaviorEnv = (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]), IORef [SomeHoldInit])
193342
194343--type role BehaviorM representational
195344-- 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
197356
198357data BehaviorSubscribed a
199− = BehaviorSubscribedHold (Hold a)
358+ = forall p. BehaviorSubscribedHold (Hold p a)
200359 | BehaviorSubscribedPull (PullSubscribed a)
201360
202361data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
@@ -213,16 +372,26 @@ data PullSubscribed a
213372data Pull a
214373 = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
215374 , pullCompute :: !(BehaviorM a)
375+#ifdef DEBUG_NODEIDS
376+ , pullNodeId :: Int
377+#endif
216378 }
217379
218380data Invalidator
219381 = forall a. InvalidatorPull (Pull a)
220382 | forall a. InvalidatorSwitch (SwitchSubscribed a)
221383
222−data RootSubscribed a
223− = RootSubscribed { rootSubscribedSubscribers :: !(IORef [WeakSubscriber a])
224− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
225− }
384+data RootSubscribed a = forall k. GCompare k => RootSubscribed
385+ { rootSubscribedKey :: !(k a)
386+ , rootSubscribedCachedSubscribed :: !(IORef (DMap k RootSubscribed)) -- From the original Root
387+ , rootSubscribedSubscribers :: !(SubscriberList a)
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
394+ }
226395
227396data Root (k :: * -> *)
228397 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
@@ -230,35 +399,102 @@ data Root (k :: * -> *)
230399 , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
231400 }
232401
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)
234409
235410-- 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
237412
238413data 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)
240− , pushSubscribedHeight :: !(IORef Int)
241− , pushSubscribedSubscribers :: !(IORef [WeakSubscriber b])
242− , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die
243− , pushSubscribedParent :: !(EventSubscribed a)
414+ = PushSubscribed { pushSubscribedCachedSubscribed :: !(IORef (Maybe (PushSubscribed a b)))
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)
416+ , pushSubscribedHeight :: !(IORef Height)
417+ , pushSubscribedSubscribers :: !(SubscriberList b)
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)))
420+ , pushSubscribedParent :: !(EventSubscription a)
244421#ifdef DEBUG_NODEIDS
245422 , pushSubscribedNodeId :: Int
246423#endif
247424 }
248425
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+
249435data 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?)
251437 , pushParent :: !(Event a)
252438 , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
253439 }
254440
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+
255488data MergeSubscribed k
256− = MergeSubscribed { mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
489+ = MergeSubscribed { mergeSubscribedCachedSubscribed :: !(IORef (Maybe (MergeSubscribed k))) -- From the original merge
490+ , mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
257491 , 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)
259− , mergeSubscribedSubscribers :: !(IORef [WeakSubscriber (DMap k Identity)])
260− , mergeSubscribedSelf :: !Any -- Hold all our Subscribers in memory
261− , mergeSubscribedParents :: !(DMap k EventSubscribed)
492+ , mergeSubscribedHeight :: !(IORef Height)
493+ , mergeSubscribedHeightBag :: !(IORef HeightBag)
494+ , mergeSubscribedSubscribers :: !(SubscriberList (DMap k Identity))
495+ , mergeSubscribedChange :: !(IORef (Subscriber (R.PatchDMap (DMap k Event)), EventSubscribed (R.PatchDMap (DMap k Event))))
496+ , mergeSubscribedParents :: !(IORef (DMap k (MergeSubscribedParent k)))
497+ , mergeSubscribedWeakSelf :: !(IORef (Weak (MergeSubscribed k)))
262498#ifdef DEBUG_NODEIDS
263499 , mergeSubscribedNodeId :: Int
264500#endif
@@ -266,28 +502,20 @@ data MergeSubscribed k
266502
267503--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
268504data Merge k
269− = Merge { mergeParents :: !(DMap k Event)
270− , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k))) --TODO: Can we replace this with an unsafePerformIO thunk?
505+ = Merge { mergeParents :: !(Dynamic R.PatchDMap (DMap k Event))
506+ , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k)))
271507 }
272508
273−data FanSubscriberKey k a where
274− FanSubscriberKey :: k a -> FanSubscriberKey k [WeakSubscriber a]
275−
276−instance GEq k => GEq (FanSubscriberKey k) where
277− geq (FanSubscriberKey a) (FanSubscriberKey b) = case geq a b of
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
509+data FanSubscribedChildren k a = FanSubscribedChildren
510+ { _fanSubscribedChildren_list :: !(SubscriberList a)
511+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed k)
512+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed k)))
513+ }
286514
287515data FanSubscribed k
288− = FanSubscribed { fanSubscribedSubscribers :: !(IORef (DMap (FanSubscriberKey k) Identity))
289− , fanSubscribedParent :: !(EventSubscribed (DMap k Identity))
290− , fanSubscribedSelf :: {-# NOUNPACK #-} (Subscriber (DMap k Identity))
516+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed k)))
517+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren k))) -- This DMap should never be empty
518+ , fanSubscribedParent :: !(EventSubscription (DMap k Identity))
291519#ifdef DEBUG_NODEIDS
292520 , fanSubscribedNodeId :: Int
293521#endif
@@ -299,16 +527,16 @@ data Fan k
299527 }
300528
301529data SwitchSubscribed a
302− = SwitchSubscribed { switchSubscribedOccurrence :: !(IORef (Maybe a))
303− , switchSubscribedHeight :: !(IORef Int)
304− , switchSubscribedSubscribers :: !(IORef [WeakSubscriber a])
305− , switchSubscribedSelf :: {-# NOUNPACK #-} (Subscriber a)
306− , switchSubscribedSelfWeak :: !(IORef (Weak (Subscriber a)))
307− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} Invalidator
530+ = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
531+ , switchSubscribedOccurrence :: !(IORef (Maybe a))
532+ , switchSubscribedHeight :: !(IORef Height)
533+ , switchSubscribedSubscribers :: !(SubscriberList a)
534+ , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !Invalidator
308535 , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
309536 , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
310537 , switchSubscribedParent :: !(Behavior (Event a))
311− , switchSubscribedCurrentParent :: !(IORef (EventSubscribed a))
538+ , switchSubscribedCurrentParent :: !(IORef (EventSubscription a))
539+ , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed a)))
312540#ifdef DEBUG_NODEIDS
313541 , switchSubscribedNodeId :: Int
314542#endif
@@ -319,13 +547,16 @@ data Switch a
319547 , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
320548 }
321549
550+{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
322551data CoincidenceSubscribed a
323− = CoincidenceSubscribed { coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
324− , coincidenceSubscribedSubscribers :: !(IORef [WeakSubscriber a])
325− , coincidenceSubscribedHeight :: !(IORef Int)
552+ = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
553+ , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
554+ , coincidenceSubscribedSubscribers :: !(SubscriberList a)
555+ , coincidenceSubscribedHeight :: !(IORef Height)
326556 , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
327− , coincidenceSubscribedOuterParent :: !(EventSubscribed (Event a))
557+ , coincidenceSubscribedOuterParent :: !(EventSubscription (Event a))
328558 , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
559+ , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed a)))
329560#ifdef DEBUG_NODEIDS
330561 , coincidenceSubscribedNodeId :: Int
331562#endif
@@ -336,41 +567,17 @@ data Coincidence a
336567 , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
337568 }
338569
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−
356570data 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?
358572 | 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)
359574 | 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)
361577 | SubscriberSwitch (SwitchSubscribed a)
362578 | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
363579 | SubscriberCoincidenceInner (CoincidenceSubscribed a)
364−
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"
580+ | SubscriberHandle
374581
375582data Event a
376583 = forall k. GCompare k => EventRoot !(k a) !(Root k)
@@ -380,16 +587,15 @@ data Event a
380587 | forall k. GCompare k => EventFan !(k a) !(Fan k)
381588 | EventSwitch !(Switch a)
382589 | EventCoincidence !(Coincidence a)
383−
384−showEventType :: Event a -> String
385−showEventType = \case
386− EventRoot _ _ -> "EventRoot"
387− EventNever -> "EventNever"
388− EventPush _ -> "EventPush"
389− EventMerge _ -> "EventMerge"
390− EventFan _ _ -> "EventFan"
391− EventSwitch _ -> "EventSwitch"
392− EventCoincidence _ -> "EventCoincidence"
590+ | forall p b. (a ~ p b, R.Patch p) => EventHold !(Hold p b)
591+ | forall p b. (a ~ p b, R.Patch p) => EventDyn !(Dyn p b)
592+ | forall b. (a ~ Identity b) => EventHoldIdentity !(Hold Identity b)
593+ | forall b. (a ~ Identity b) => EventDynIdentity !(Dyn Identity b)
594+
595+data EventSubscription a = EventSubscription
596+ { _eventSubscription_ticket :: !(Maybe (SubscriberListTicket a))
597+ , _eventSubscription_subscribed :: !(EventSubscribed a)
598+ }
393599
394600data EventSubscribed a
395601 = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
@@ -400,6 +606,9 @@ data EventSubscribed a
400606 | EventSubscribedSwitch !(SwitchSubscribed a)
401607 | EventSubscribedCoincidence !(CoincidenceSubscribed a)
402608
609+unsubscribe :: EventSubscription a -> IO ()
610+unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
611+
403612-- These function are constructor functions that are marked NOINLINE so they are
404613-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
405614-- so any weak references that are attached to the constructors will have their
@@ -409,22 +618,30 @@ data EventSubscribed a
409618-- The result is also evaluated to WHNF, since forcing a thunk invalidates
410619-- the weak pointer to it in some cases.
411620
412−{-# NOINLINE newRootSubscribed #-}
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−
621+--TODO: I think all of these noinlines are now unnecessary
420622{-# 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)
422624newSubscriberPush compute subd = return $! SubscriberPush compute subd
423625
626+{-# RULES
627+ "newSubscriberHold/Identity" forall h. newSubscriberHold h = newSubscriberHoldIdentity h
628+ #-}
424629{-# NOINLINE newSubscriberHold #-}
425−newSubscriberHold :: Hold a -> IO (Subscriber a)
630+newSubscriberHold :: R.Patch p => Hold p a -> IO (Subscriber (p a))
426631newSubscriberHold h = return $! SubscriberHold h
427632
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+
428645{-# NOINLINE newSubscriberFan #-}
429646newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
430647newSubscriberFan subd = return $! SubscriberFan subd
@@ -449,15 +666,100 @@ newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
449666newInvalidatorPull :: Pull a -> IO Invalidator
450667newInvalidatorPull p = return $! InvalidatorPull p
451668
452−{-# NOINLINE newBox #-}
453−newBox :: a -> IO (Box a)
454−newBox a = return $! Box a
669+data Dynamic p a
670+ = DynamicHold !(Hold p 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'
455754
456755--type role Behavior representational
457756data Behavior a
458− = BehaviorHold !(Hold a)
757+ = forall p. BehaviorHold !(Hold p a)
758+ | BehaviorHoldIdentity !(Hold Identity a)
459759 | BehaviorConst !a
460760 | BehaviorPull !(Pull a)
761+ | BehaviorDynIdentity !(Dyn Identity a)
762+ | forall p. R.Patch p => BehaviorDyn !(Dyn p a)
461763
462764-- ResultM can read behaviors and events
463765type ResultM = EventM
@@ -470,24 +772,26 @@ instance Functor Event where
470772 fmap f = push $ return . Just . f
471773
472774instance Functor Behavior where
473− fmap f = pull . liftM f . readBehaviorTracked
775+ fmap f = pull . fmap f . readBehaviorTracked
474776
475777{-# 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
477779push f e = EventPush $ Push
478780 { pushCompute = f
479781 , pushParent = e
480782 , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
481783 }
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 #-}
483785
484786{-# NOINLINE pull #-}
485787pull :: BehaviorM a -> Behavior a
486788pull a = BehaviorPull $ Pull
487789 { pullCompute = a
488790 , pullValue = unsafeNewIORef a Nothing
791+#ifdef DEBUG_NODEIDS
792+ , pullNodeId = unsafeNodeId a
793+#endif
489794 }
490−{-# RULES "pull/pull" forall a. pull (readBehaviorTracked (pull a)) = pull a #-}
491795
492796{-# NOINLINE switch #-}
493797switch :: Behavior (Event a) -> Event a
@@ -495,7 +799,6 @@ switch a = EventSwitch $ Switch
495799 { switchParent = a
496800 , switchSubscribed = unsafeNewIORef a Nothing
497801 }
498−{-# RULES "switch/constB" forall e. switch (BehaviorConst e) = e #-}
499802
500803coincidence :: Event (Event a) -> Event a
501804coincidence a = EventCoincidence $ Coincidence
@@ -503,32 +806,20 @@ coincidence a = EventCoincidence $ Coincidence
503806 , coincidenceSubscribed = unsafeNewIORef a Nothing
504807 }
505808
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 ()
517810propagateAndUpdateSubscribersRef 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
522812
523813-- 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
525815run roots after = do
526− when debugPropagate $ putStrLn "Running an event frame"
527− result <- runFrame $ do
816+ tracePropagate $ "Running an event frame with " <> show (length roots) <> " events"
817+ spiderEnv <- SpiderHost ask
818+ result <- SpiderHost $ lift $ withMVar (_spiderEnv_lock spiderEnv) $ \_ -> flip runReaderT spiderEnv $ unSpiderHost $ runFrame $ do
528819 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
529820 occBefore <- liftIO $ do
530821 occBefore <- readIORef occRef
531− writeIORef occRef $ DMap.insert k a occBefore
822+ writeIORef occRef $! DMap.insert k a occBefore
532823 return occBefore
533824 if DMap.null occBefore
534825 then do scheduleRootClear occRef
@@ -541,19 +832,19 @@ run roots after = do
541832 delayed <- liftIO $ readIORef delayedRef
542833 case IntMap.minViewWithKey delayed of
543834 Nothing -> return ()
544− Just ((currentHeight, current), future) -> do
545− when debugPropagate $ liftIO $ putStrLn $ "Running height " ++ show currentHeight
546− putCurrentHeight currentHeight
547− liftIO $ writeIORef delayedRef future
548− forM_ current $ \d -> case d of
835+ Just ((currentHeight, cur), future) -> do
836+ tracePropagate $ "Running height " ++ show currentHeight
837+ putCurrentHeight $ Height currentHeight
838+ liftIO $ writeIORef delayedRef $! future
839+ forM_ cur $ \d -> case d of
549840 DelayedMerge subscribed -> do
550841 height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
551− case height `compare` currentHeight of
842+ case height `compare` Height currentHeight of
552843 LT -> error "Somehow a merge's height has been decreased after it was scheduled"
553844 GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
554845 EQ -> do
555846 m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
556− liftIO $ writeIORef (mergeSubscribedAccum subscribed) DMap.empty
847+ liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! DMap.empty
557848 --TODO: Assert that m is not empty
558849 liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
559850 scheduleClear $ mergeSubscribedOccurrence subscribed
@@ -562,14 +853,14 @@ run roots after = do
562853 go
563854 putCurrentHeight maxBound
564855 after
565− when debugPropagate $ putStrLn "Done running an event frame"
856+ tracePropagate "Done running an event frame"
566857 return result
567858
568−data SomeMaybeIORef = forall a. SomeMaybeIORef (IORef (Maybe a))
859+data SomeClear = forall a. SomeClear (IORef (Maybe a))
569860
570−data SomeDMapIORef = forall k. SomeDMapIORef (IORef (DMap k Identity))
861+data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
571862
572−data SomeAssignment = forall a. SomeAssignment (Hold a) a
863+data SomeAssignment = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak Invalidator]) a
573864
574865data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
575866
@@ -577,136 +868,241 @@ debugFinalize :: Bool
577868debugFinalize = False
578869
579870mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
580−mkWeakPtrWithDebug x debugNote = mkWeakPtr x $
581− if debugFinalize
582− then Just $ putStrLn $ "finalizing: " ++ debugNote
583− else Nothing
871+mkWeakPtrWithDebug x debugNote = do
872+ x' <- evaluate x
873+ mkWeakPtr x' $
874+ if debugFinalize
875+ then Just $ putStrLn $ "finalizing: " ++ debugNote
876+ else Nothing
584877
585878type WeakList a = [Weak a]
586879
587−--TODO: Is it faster to clean up every time, or to occasionally go through and clean up as needed?
588−traverseAndCleanWeakList_ :: Monad m => (wa -> m (Maybe a)) -> [wa] -> (a -> m ()) -> m [wa]
589−traverseAndCleanWeakList_ deRef ws f = go ws
590− where go [] = return []
591− go (h:t) = do
592− ma <- deRef h
593− case ma of
594− Just a -> do
595− f a
596− t' <- go t
597− return $ h : t'
598− Nothing -> go t
880+{-# INLINE withIncreasedDepth #-}
881+#ifdef DEBUG
882+withIncreasedDepth :: CanTrace x m => m a -> m a
883+withIncreasedDepth a = do
884+ spiderEnv <- askSpiderEnv
885+ liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) succ
886+ result <- a
887+ liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) pred
888+ return result
889+#else
890+withIncreasedDepth :: m a -> m a
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
599959
600960-- | Propagate everything at the current height
601−propagate :: a -> [WeakSubscriber a] -> EventM [WeakSubscriber a]
602−propagate a subscribers = do
603− traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subscribers $ \s -> case s of
604− SubscriberPush compute subscribed -> do
605− when debugPropagate $ liftIO $ putStrLn $ "SubscriberPush" <> showNodeId subscribed
606− occ <- compute a
961+propagate :: a -> SubscriberList a -> EventM x ()
962+propagate a subscribers = withIncreasedDepth $ do
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
966+ SubscriberPush compute subscribed -> {-# SCC "traversePush" #-} do
967+ tracePropagate $ "SubscriberPush" <> showNodeId subscribed
968+ occ <- runComputeM $ compute a
607969 case occ of
608970 Nothing -> return () -- No need to write a Nothing back into the Ref
609971 Just o -> do
610972 liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
611973 scheduleClear $ pushSubscribedOccurrence subscribed
612− liftIO . writeIORef (pushSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (pushSubscribedSubscribers subscribed))
613− SubscriberMerge k subscribed -> do
614− when debugPropagate $ liftIO $ putStrLn $ "SubscriberMerge" <> showNodeId subscribed
974+ propagate o $ pushSubscribedSubscribers subscribed
975+ SubscriberMerge k subscribed -> {-# SCC "traverseMerge" #-} do
976+ tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
615977 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
617981 when (DMap.null oldM) $ do -- Only schedule the firing once
618982 height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
619983 --TODO: assertions about height
620984 currentHeight <- getCurrentHeight
621− when (height <= currentHeight) $ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")"
985+ when (height <= currentHeight) $ do
986+ myStack <- liftIO $ whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
987+ if height /= invalidHeight
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)
622993 scheduleMerge height subscribed
623− SubscriberFan subscribed -> do
994+ SubscriberMergeChange subscribed -> {-# SCC "traverseMergeChange" #-} do
995+ tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
996+ defer $ SomeMergeUpdate a subscribed
997+ SubscriberFan subscribed -> {-# SCC "traverseFan" #-} do
624998 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"
6261000 --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
6281002 Nothing -> do
629− when debugPropagate $ liftIO $ putStrLn "No subscriber for key"
1003+ tracePropagate "No subscriber for key"
6301004 return ()
631− Just (Identity subsubs) -> do
632− _ <- propagate v subsubs --TODO: use the value of this
1005+ Just subsubs -> do
1006+ propagate v $ _fanSubscribedChildren_list subsubs
6331007 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
642− invalidators <- liftIO $ readIORef $ holdInvalidators h
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
647− when debugPropagate $ liftIO $ putStrLn $ "SubscriberSwitch" <> showNodeId subscribed
1008+ SubscriberHold h -> {-# SCC "traverseHold" #-} propagateSubscriberHold h a
1009+ SubscriberHoldIdentity h -> {-# SCC "traverseHoldIdentity" #-} propagateSubscriberHold h a
1010+ SubscriberSwitch subscribed -> {-# SCC "traverseSwitch" #-} do
1011+ tracePropagate $ "SubscriberSwitch" <> showNodeId subscribed
6481012 liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
6491013 scheduleClear $ switchSubscribedOccurrence subscribed
650− subs <- liftIO $ readIORef $ switchSubscribedSubscribers subscribed
651− liftIO . writeIORef (switchSubscribedSubscribers subscribed) =<< propagate a subs
652− SubscriberCoincidenceOuter subscribed -> do
653− when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
1014+ propagate a $ switchSubscribedSubscribers subscribed
1015+ SubscriberCoincidenceOuter subscribed -> {-# SCC "traverseCoincidenceOuter" #-} do
1016+ tracePropagate $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
6541017 outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
655− when debugPropagate $ liftIO $ putStrLn $ " outerHeight = " <> show outerHeight
1018+ tracePropagate $ " outerHeight = " <> show outerHeight
6561019 (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
657− when debugPropagate $ liftIO $ putStrLn $ " isJust occ = " <> show (isJust occ)
658− when debugPropagate $ liftIO $ putStrLn $ " innerHeight = " <> show innerHeight
1020+ tracePropagate $ " isJust occ = " <> show (isJust occ)
1021+ tracePropagate $ " innerHeight = " <> show innerHeight
6591022 liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
6601023 scheduleClear $ coincidenceSubscribedInnerParent subscribed
6611024 case occ of
6621025 Nothing -> do
6631026 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
664− writeIORef (coincidenceSubscribedHeight subscribed) innerHeight
665− mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
666− mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
1027+ writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
1028+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
1029+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
6671030 Just o -> do -- Since it's already firing, no need to adjust height
6681031 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
6691032 scheduleClear $ coincidenceSubscribedOccurrence subscribed
670− liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
671− SubscriberCoincidenceInner subscribed -> do
672− when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceInner" <> showNodeId subscribed
673− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
674− scheduleClear $ coincidenceSubscribedOccurrence subscribed
675− liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate a =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
1033+ propagate o $ coincidenceSubscribedSubscribers subscribed
1034+ SubscriberCoincidenceInner subscribed -> {-# SCC "traverseCoincidenceInner" #-} do
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
1040+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
1041+ scheduleClear $ coincidenceSubscribedOccurrence 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'
6761059
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
6781061
679−subscribeCoincidenceInner :: Event a -> Int -> CoincidenceSubscribed a -> EventM (Maybe a, Int, EventSubscribed a)
1062+{-# INLINE subscribeCoincidenceInner #-}
1063+subscribeCoincidenceInner :: CanSubscribe x m => Event a -> Height -> CoincidenceSubscribed a -> m (Maybe a, Height, EventSubscribed a)
6801064subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
6811065 subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
682− wsubInner <- liftIO $ mkWeakPtrWithDebug subInner "SubscriberCoincidenceInner"
683− innerSubd <- {-# SCC "innerSubd" #-} (subscribe o $ WeakSubscriberSimple wsubInner)
684− innerOcc <- liftIO $ getEventSubscribedOcc innerSubd
1066+ subscription@(EventSubscription _ innerSubd) <- subscribe o subInner
1067+ innerOcc <- liftIO $ readEventSubscribed innerSubd
6851068 innerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef innerSubd
6861069 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
6881071 return (innerOcc, height, innerSubd)
6891072
690−readBehavior :: Behavior a -> IO a
691−readBehavior b = runBehaviorM (readBehaviorTracked b) Nothing --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
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
1078+ liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
6921079
693−runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IO a
694−runBehaviorM a mwi = runReaderT (unBehaviorM a) mwi
1080+runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IORef [SomeHoldInit] -> IO a
1081+runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
6951082
6961083askInvalidator :: 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
6981089
6991090askParentsRef :: 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
7011101
7021102readBehaviorTracked :: Behavior a -> BehaviorM a
7031103readBehaviorTracked b = case b of
704− BehaviorHold h -> do
705− result <- liftIO $ readIORef $ holdValue 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
1104+ BehaviorHold h -> readHoldTracked h
1105+ BehaviorHoldIdentity h -> readHoldTracked h
7101106 BehaviorConst a -> return a
7111107 BehaviorPull p -> do
7121108 val <- liftIO $ readIORef $ pullValue p
@@ -720,7 +1116,8 @@ readBehaviorTracked b = case b of
7201116 i <- liftIO $ newInvalidatorPull p
7211117 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
7221118 parentsRef <- liftIO $ newIORef []
723− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
1119+ holdInits <- askBehaviorHoldInits
1120+ a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
7241121 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
7251122 parents <- liftIO $ readIORef parentsRef
7261123 let subscribed = PullSubscribed
@@ -732,299 +1129,492 @@ readBehaviorTracked b = case b of
7321129 liftIO $ writeIORef (pullValue p) $ Just subscribed
7331130 askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
7341131 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
7351155
1156+{-
7361157readEvent :: Event a -> ResultM (Maybe a)
7371158readEvent 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
7391160 EventNever -> return Nothing
7401161 EventPush p -> do
7411162 subscribed <- getPushSubscribed p
742− liftIO $ do
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
1163+ liftIO $ readPushSubscribed subscribed
7461164 EventMerge m -> do
7471165 subscribed <- getMergeSubscribed m
748− liftIO $ do
749− result <- readIORef $ mergeSubscribedOccurrence subscribed
750− touch $ mergeSubscribedSelf subscribed
751− return result
1166+ liftIO $ readMergeSubscribed subscribed
7521167 EventFan k f -> do
753− parentOcc <- readEvent $ fanParent f
754− return . coerce $ DMap.lookup k =<< parentOcc
1168+ subscribed <- getFanSubscribed f
1169+ liftIO $ readFanSubscribed k subscribed
7551170 EventSwitch s -> do
7561171 subscribed <- getSwitchSubscribed s
757− liftIO $ do
758− result <- readIORef $ switchSubscribedOccurrence subscribed
759− touch $ switchSubscribedSelf subscribed
760− touch $ switchSubscribedOwnInvalidator subscribed
761− return result
1172+ liftIO $ readSwitchSubscribed subscribed
7621173 EventCoincidence c -> do
7631174 subscribed <- getCoincidenceSubscribed c
764− liftIO $ do
765− result <- readIORef $ coincidenceSubscribedOccurrence subscribed
766− touch $ coincidenceSubscribedOuter subscribed
767− --TODO: do we need to touch the inner subscriber?
768− return result
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
1220+ result <- readIORef $ coincidenceSubscribedOccurrence subscribed
1221+ touch $ coincidenceSubscribedOuter subscribed
1222+ --TODO: do we need to touch the inner subscriber?
1223+ return result
7691224
7701225-- Always refers to 0
7711226{-# NOINLINE zeroRef #-}
772−zeroRef :: IORef Int
773−zeroRef = unsafePerformIO $ newIORef 0
774−
775−getEventSubscribed :: Event a -> EventM (EventSubscribed a)
776−getEventSubscribed e = case e of
777− EventRoot k r -> liftM EventSubscribedRoot $ getRootSubscribed k r
778− EventNever -> return EventSubscribedNever
779− EventPush p -> liftM EventSubscribedPush $ getPushSubscribed p
780− EventFan k f -> liftM (EventSubscribedFan k) $ getFanSubscribed f
781− EventMerge m -> liftM EventSubscribedMerge $ getMergeSubscribed m
782− EventSwitch s -> liftM EventSubscribedSwitch $ getSwitchSubscribed s
783− EventCoincidence c -> liftM EventSubscribedCoincidence $ getCoincidenceSubscribed c
784−
1227+zeroRef :: IORef Height
1228+zeroRef = unsafePerformIO $ newIORef zeroHeight
1229+
1230+{-# SPECIALIZE subscribe :: Event a -> Subscriber a -> EventM x (EventSubscription a) #-}
1231+subscribe :: forall x m a. CanSubscribe x m => Event a -> Subscriber a -> m (EventSubscription a)
1232+subscribe e sub = case e of
1233+ EventRoot k r -> wrap EventSubscribedRoot $ liftIO . getRootSubscribed k r
1234+ EventNever -> return $ EventSubscription Nothing EventSubscribedNever
1235+ EventPush p -> wrap EventSubscribedPush $ getPushSubscribed p
1236+ EventFan k f -> wrap (EventSubscribedFan k) $ getFanSubscribed k f
1237+ EventMerge m -> wrap EventSubscribedMerge $ getMergeSubscribed m
1238+ EventSwitch s -> wrap EventSubscribedSwitch $ getSwitchSubscribed s
1239+ EventCoincidence c -> wrap EventSubscribedCoincidence $ getCoincidenceSubscribed c
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 #-}
7851258debugSubscribe :: Bool
7861259debugSubscribe = False
7871260
788−subscribeEventSubscribed :: EventSubscribed a -> WeakSubscriber a -> IO ()
789−subscribeEventSubscribed es ws = case es of
1261+{-# INLINE subscribeEventSubscribed #-}
1262+subscribeEventSubscribed :: EventSubscribed a -> Subscriber a -> IO (Maybe (SubscriberListTicket a))
1263+subscribeEventSubscribed es sub = case es of
7901264 EventSubscribedRoot r -> do
791− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Root"
792− modifyIORef' (rootSubscribedSubscribers r) (ws:)
1265+ traceSubscribe $ "subscribeEventSubscribed Root"
1266+ Just <$> subscribeRootSubscribed r sub
7931267 EventSubscribedNever -> do
794− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Never"
795− return ()
1268+ traceSubscribe $ "subscribeEventSubscribed Never"
1269+ return Nothing
7961270 EventSubscribedPush subscribed -> do
797− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Push"
798− modifyIORef' (pushSubscribedSubscribers subscribed) (ws:)
1271+ traceSubscribe $ "subscribeEventSubscribed Push"
1272+ Just <$> subscribePushSubscribed subscribed sub
7991273 EventSubscribedFan k subscribed -> do
800− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Fan"
801− modifyIORef' (fanSubscribedSubscribers subscribed) $ DMap.insertWith (liftA2 (++)) (FanSubscriberKey k) (Identity [ws])
1274+ traceSubscribe $ "subscribeEventSubscribed Fan"
1275+ Just <$> subscribeFanSubscribed k subscribed sub
8021276 EventSubscribedMerge subscribed -> do
803− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Merge"
804− modifyIORef' (mergeSubscribedSubscribers subscribed) (ws:)
1277+ traceSubscribe $ "subscribeEventSubscribed Merge"
1278+ Just <$> subscribeMergeSubscribed subscribed sub
8051279 EventSubscribedSwitch subscribed -> do
806− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Switch"
807− modifyIORef' (switchSubscribedSubscribers subscribed) (ws:)
1280+ traceSubscribe $ "subscribeEventSubscribed Switch"
1281+ Just <$> subscribeSwitchSubscribed subscribed sub
8081282 EventSubscribedCoincidence subscribed -> do
809− when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Coincidence"
810− modifyIORef' (coincidenceSubscribedSubscribers subscribed) (ws:)
1283+ traceSubscribe $ "subscribeEventSubscribed Coincidence"
1284+ Just <$> subscribeCoincidenceSubscribed subscribed sub
8111285
812−getEventSubscribedOcc :: EventSubscribed a -> IO (Maybe a)
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
826−eventSubscribedHeightRef es = case es of
1286+{-# INLINE eventSubscribedHeightRef #-}
1287+eventSubscribedHeightRef :: EventSubscribed a -> IORef Height
1288+eventSubscribedHeightRef = \case
8271289 EventSubscribedRoot _ -> zeroRef
8281290 EventSubscribedNever -> zeroRef
8291291 EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
830− EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ fanSubscribedParent subscribed
1292+ EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
8311293 EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
8321294 EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
8331295 EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
8341296
835−subscribe :: Event a -> WeakSubscriber a -> EventM (EventSubscribed a)
836−subscribe e ws = do
837− subd <- getEventSubscribed e
838− liftIO $ subscribeEventSubscribed subd ws
839− return subd
1297+{-# INLINE eventSubscribedHasOwnHeightRef #-}
1298+eventSubscribedHasOwnHeightRef :: EventSubscribed a -> Bool
1299+eventSubscribedHasOwnHeightRef = \case
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
1364+ return (sln, subscribed)
8401365
841−noinlineFalse :: Bool
842−noinlineFalse = False
843−{-# NOINLINE noinlineFalse #-}
1366+{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1367+cleanupRootSubscribed :: RootSubscribed a -> IO ()
1368+cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1369+ rootSubscribedUninit self
1370+ modifyIORef' cached $ DMap.delete k
8441371
845−getRootSubscribed :: GCompare k => k a -> Root k -> EventM (RootSubscribed a)
846−getRootSubscribed k r = do
847− mSubscribed <- liftIO $ readIORef $ rootSubscribed r
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
1372+{-# INLINE subscribeRootSubscribed #-}
1373+subscribeRootSubscribed :: RootSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1374+subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
8611375
8621376-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
863−getPushSubscribed :: Push a b -> EventM (PushSubscribed a b)
864−getPushSubscribed p = do
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)
1379+getPushSubscribed p sub = do
8651380 mSubscribed <- liftIO $ readIORef $ pushSubscribed p
8661381 case mSubscribed of
867− Just subscribed -> return subscribed
868− Nothing -> do -- Not yet subscribed
869− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ pushSubscribed p
1382+ Just subscribed -> {-# SCC "hitPush" #-} do
1383+ sln <- liftIO $ subscribePushSubscribed subscribed sub
1384+ return (sln, subscribed)
1385+ Nothing -> {-# SCC "missPush" #-} do -- Not yet subscribed
1386+ subscribedRef <- liftIO $ newIORef $ error "getPushSubscribed: subscribed not yet intialized"
1387+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
8701388 s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
871− ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberPush"
872− subd <- subscribe (pushParent p) $ WeakSubscriberSimple ws
873− parentOcc <- liftIO $ getEventSubscribedOcc subd
874− occ <- liftM join $ mapM (pushCompute p) parentOcc
1389+ subscription@(EventSubscription _ subd) <- subscribe (pushParent p) s
1390+ parentOcc <- liftIO $ readEventSubscribed subd
1391+ occ <- runComputeM $ join <$> mapM (pushCompute p) parentOcc
8751392 occRef <- liftIO $ newIORef occ
8761393 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
8781397 let subscribed = PushSubscribed
879− { pushSubscribedOccurrence = occRef
1398+ { pushSubscribedCachedSubscribed = cached
1399+ , pushSubscribedOccurrence = occRef
8801400 , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
881− , pushSubscribedSubscribers = subscribersRef
882− , pushSubscribedSelf = unsafeCoerce s
883− , pushSubscribedParent = subd
1401+ , pushSubscribedSubscribers = subs
1402+ , pushSubscribedSelf = s
1403+ , pushSubscribedWeakSelf = weakSelf
1404+ , pushSubscribedParent = subscription
8841405#ifdef DEBUG_NODEIDS
8851406 , pushSubscribedNodeId = unsafeNodeId p
8861407#endif
8871408 }
1409+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
1410+ liftIO $ writeIORef subscribedRef $! subscribed
8881411 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
8901422
891−getMergeSubscribed :: forall k. GCompare k => Merge k -> EventM (MergeSubscribed k)
892−getMergeSubscribed m = {-# SCC "getMergeSubscribed.entire" #-} do
1423+{-# SPECIALIZE getMergeSubscribed :: GCompare k => Merge k -> Subscriber (DMap k Identity) -> EventM x (SubscriberListTicket (DMap k Identity), 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)
1425+getMergeSubscribed m sub = do
8931426 mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
8941427 case mSubscribed of
895− Just subscribed -> return subscribed
896− Nothing -> if DMap.null $ mergeParents m then emptyMergeSubscribed else do
1428+ Just subscribed -> {-# SCC "hitMerge" #-} do
1429+ sln <- liftIO $ subscribeMergeSubscribed subscribed sub
1430+ return (sln, subscribed)
1431+ Nothing -> {-# SCC "missMerge" #-} do
8971432 subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
8981433 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
899− s <- liftIO $ newBox subscribedUnsafe
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
902− parentSubd <- {-# SCC "getMergeSubscribed.a.parentSubd" #-} subscribe e $ WeakSubscriberMerge k ws
903− parentOcc <- {-# SCC "getMergeSubscribed.a.parentOcc" #-} liftIO $ getEventSubscribedOcc parentSubd
904− height <- {-# SCC "getMergeSubscribed.a.height" #-} liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
905− return $ {-# SCC "getMergeSubscribed.a.returnVal" #-} (unsafeCoerce s :: Any, fmap (\x -> k :=> Identity x) parentOcc, height, k :=> parentSubd)
906− let dm = DMap.fromDistinctAscList $ catMaybes $ map (\(_, x, _, _) -> x) subscribers
907− subscriberHeights = map (\(_, _, x, _) -> x) subscribers
908− myHeight =
909− if any (==invalidHeight) subscriberHeights --TODO: Replace 'any' with invalidHeight-preserving 'maximum'
910− then invalidHeight
911− else succ $ Prelude.maximum subscriberHeights -- This is safe because the DMap will never be empty here
1434+ initialParents <- readBehaviorUntracked $ current $ mergeParents m
1435+ subscribers :: [(Maybe (DSum k Identity), Height, DSum k (MergeSubscribedParent k))] <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1436+ s <- liftIO $ newSubscriberMerge k subscribedUnsafe
1437+ subscription@(EventSubscription _ parentSubd) <- subscribe e s
1438+ parentOcc <- liftIO $ readEventSubscribed parentSubd
1439+ height <- liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
1440+ return $ (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription s)
1441+ let dm = DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1442+ heights = fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1443+ myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1444+ myHeight = if invalidHeight `elem` heights
1445+ then invalidHeight
1446+ else succHeight $ heightBagMax myHeightBag
9121447 currentHeight <- getCurrentHeight
9131448 let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
9141449 then (if DMap.null dm then Nothing else Just dm, DMap.empty)
9151450 else (Nothing, dm)
916− when (not $ DMap.null accum) $ do
1451+ unless (DMap.null accum) $ do
9171452 scheduleMerge myHeight subscribedUnsafe
9181453 occRef <- liftIO $ newIORef occ
9191454 when (isJust occ) $ scheduleClear occRef
9201455 accumRef <- liftIO $ newIORef accum
9211456 heightRef <- liftIO $ newIORef myHeight
922− subsRef <- liftIO $ newIORef []
923− let subscribed = MergeSubscribed
924− { mergeSubscribedOccurrence = occRef
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
1462+ let !subscribed = MergeSubscribed
1463+ { mergeSubscribedCachedSubscribed = mergeSubscribed m
1464+ , mergeSubscribedOccurrence = occRef
9251465 , mergeSubscribedAccum = accumRef
9261466 , mergeSubscribedHeight = heightRef
927− , mergeSubscribedSubscribers = subsRef
928− , mergeSubscribedSelf = unsafeCoerce $ map (\(x, _, _, _) -> x) subscribers --TODO: Does lack of strictness make this leak?
929− , mergeSubscribedParents = DMap.fromDistinctAscList $ map (\(_, _, _, x) -> x) subscribers
1467+ , mergeSubscribedHeightBag = heightMapRef
1468+ , mergeSubscribedSubscribers = subs
1469+ , mergeSubscribedChange = changeSubdRef
1470+ , mergeSubscribedParents = parentsRef
1471+ , mergeSubscribedWeakSelf = weakSelf
9301472#ifdef DEBUG_NODEIDS
9311473 , mergeSubscribedNodeId = unsafeNodeId m
9321474#endif
9331475 }
934− liftIO $ writeIORef subscribedRef subscribed
935− return subscribed
936− where emptyMergeSubscribed = do --TODO: This should never happen
937− occRef <- liftIO $ newIORef Nothing
938− accumRef <- liftIO $ newIORef DMap.empty
939− subsRef <- liftIO $ newIORef []
940− return $ MergeSubscribed
941− { mergeSubscribedOccurrence = occRef
942− , mergeSubscribedAccum = accumRef
943− , mergeSubscribedHeight = zeroRef
944− , mergeSubscribedSubscribers = subsRef --TODO: This will definitely leak
945− , mergeSubscribedSelf = unsafeCoerce ()
946− , mergeSubscribedParents = DMap.empty
947−#ifdef DEBUG_NODEIDS
948− , mergeSubscribedNodeId = -1
949−#endif
950− }
951−
952−getFanSubscribed :: GCompare k => Fan k -> EventM (FanSubscribed k)
953−getFanSubscribed f = do
1476+ defer $ SomeMergeInit subscribed $ updated $ mergeParents m
1477+ liftIO $ writeIORef subscribedRef $! subscribed
1478+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "MergeSubscribed"
1479+ liftIO $ writeIORef (mergeSubscribed m) $ Just subscribed
1480+ return (sln, subscribed)
1481+
1482+cleanupMergeSubscribed :: MergeSubscribed k -> IO ()
1483+cleanupMergeSubscribed subscribed = do
1484+ parents <- readIORef $ mergeSubscribedParents subscribed
1485+ forM_ (DMap.toList parents) $ \(_ :=> msp) -> unsubscribe $ mergeSubscribedParentSubscription msp
1486+ -- Not necessary, because this whole MergeSubscribed is dead: writeIORef (mergeSubscribedParents subscribed) DMap.empty
1487+ writeIORef (mergeSubscribedCachedSubscribed subscribed) $ Nothing -- Get rid of the cached subscribed
1488+
1489+{-# INLINE subscribeMergeSubscribed #-}
1490+subscribeMergeSubscribed :: MergeSubscribed k -> Subscriber (DMap k Identity) -> IO (SubscriberListTicket (DMap k Identity))
1491+subscribeMergeSubscribed subscribed sub = WeakBag.insert sub (mergeSubscribedSubscribers subscribed) (mergeSubscribedWeakSelf subscribed) cleanupMergeSubscribed
1492+
1493+{-# SPECIALIZE getFanSubscribed :: GCompare k => k a -> Fan k -> Subscriber a -> EventM x (SubscriberListTicket a, FanSubscribed k) #-}
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
9541496 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
9551497 case mSubscribed of
956− Just subscribed -> return subscribed
957− Nothing -> do
958− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ fanSubscribed f
959− sub <- liftIO $ newSubscriberFan subscribedUnsafe
960− wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberFan"
961− subd <- subscribe (fanParent f) $ WeakSubscriberSimple wsub
962− subscribersRef <- liftIO $ newIORef DMap.empty
1498+ Just subscribed -> {-# SCC "hitFan" #-} do
1499+ sln <- liftIO $ subscribeFanSubscribed k subscribed sub
1500+ return (sln, subscribed)
1501+ Nothing -> {-# SCC "missFan" #-} do
1502+ subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1503+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1504+ s <- liftIO $ newSubscriberFan subscribedUnsafe
1505+ subscription <- subscribe (fanParent f) s
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"
9631509 let subscribed = FanSubscribed
964− { fanSubscribedParent = subd
1510+ { fanSubscribedCachedSubscribed = fanSubscribed f
1511+ , fanSubscribedParent = subscription
9651512 , fanSubscribedSubscribers = subscribersRef
966− , fanSubscribedSelf = sub
9671513#ifdef DEBUG_NODEIDS
9681514 , fanSubscribedNodeId = unsafeNodeId f
9691515#endif
9701516 }
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
9711521 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
972− return subscribed
973−
974−getSwitchSubscribed :: Switch a -> EventM (SwitchSubscribed a)
975−getSwitchSubscribed s = do
1522+ return (slnForSub, subscribed)
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+
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)
1550+getSwitchSubscribed s sub = do
9761551 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
9771552 case mSubscribed of
978− Just subscribed -> return subscribed
979− Nothing -> do
1553+ Just subscribed -> {-# SCC "hitSwitch" #-} do
1554+ sln <- liftIO $ subscribeSwitchSubscribed subscribed sub
1555+ return (sln, subscribed)
1556+ Nothing -> {-# SCC "missSwitch" #-} do
9801557 subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
9811558 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
9821559 i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
983− sub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1560+ mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
9841561 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
9851562 wiRef <- liftIO $ newIORef wi
986− wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberSwitch"
987− selfWeakRef <- liftIO $ newIORef wsub
9881563 parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
989− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) $ Just (wi, parentsRef)
990− subd <- subscribe e $ WeakSubscriberSimple wsub
991− subdRef <- liftIO $ newIORef subd
992− parentOcc <- liftIO $ getEventSubscribedOcc subd
1564+ holdInits <- getDeferralQueue
1565+ e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1566+ subscription@(EventSubscription _ subd) <- subscribe e mySub
1567+ subscriptionRef <- liftIO $ newIORef subscription
1568+ parentOcc <- liftIO $ readEventSubscribed subd
9931569 occRef <- liftIO $ newIORef parentOcc
9941570 when (isJust parentOcc) $ scheduleClear occRef
9951571 heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
996− subscribersRef <- liftIO $ newIORef []
997− let subscribed = SwitchSubscribed
998− { switchSubscribedOccurrence = occRef
1572+ weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1573+ (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1574+ let !subscribed = SwitchSubscribed
1575+ { switchSubscribedCachedSubscribed = switchSubscribed s
1576+ , switchSubscribedOccurrence = occRef
9991577 , switchSubscribedHeight = heightRef
1000− , switchSubscribedSubscribers = subscribersRef
1001− , switchSubscribedSelf = sub
1002− , switchSubscribedSelfWeak = selfWeakRef
1578+ , switchSubscribedSubscribers = subs
10031579 , switchSubscribedOwnInvalidator = i
10041580 , switchSubscribedOwnWeakInvalidator = wiRef
10051581 , switchSubscribedBehaviorParents = parentsRef
10061582 , switchSubscribedParent = switchParent s
1007− , switchSubscribedCurrentParent = subdRef
1583+ , switchSubscribedCurrentParent = subscriptionRef
1584+ , switchSubscribedWeakSelf = weakSelf
10081585#ifdef DEBUG_NODEIDS
10091586 , switchSubscribedNodeId = unsafeNodeId s
10101587#endif
10111588 }
1012− liftIO $ writeIORef subscribedRef subscribed
1589+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1590+ liftIO $ writeIORef subscribedRef $! subscribed
10131591 liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1014− return subscribed
1592+ return (slnForSub, subscribed)
10151593
1016−getCoincidenceSubscribed :: forall a. Coincidence a -> EventM (CoincidenceSubscribed a)
1017−getCoincidenceSubscribed c = do
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)
1606+getCoincidenceSubscribed c sub = do
10181607 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
10191608 case mSubscribed of
1020− Just subscribed -> return subscribed
1021− Nothing -> do
1609+ Just subscribed -> {-# SCC "hitCoincidence" #-} do
1610+ sln <- liftIO $ subscribeCoincidenceSubscribed subscribed sub
1611+ return (sln, subscribed)
1612+ Nothing -> {-# SCC "missCoincidence" #-} do
10221613 subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
10231614 subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
10241615 subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1025− wsubOuter <- liftIO $ mkWeakPtrWithDebug subOuter "subOuter"
1026− outerSubd <- subscribe (coincidenceParent c) $ WeakSubscriberSimple wsubOuter
1027− outerOcc <- liftIO $ getEventSubscribedOcc outerSubd
1616+ outerSubscription@(EventSubscription _ outerSubd) <- subscribe (coincidenceParent c) subOuter
1617+ outerOcc <- liftIO $ readEventSubscribed outerSubd
10281618 outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
10291619 (occ, height, mInnerSubd) <- case outerOcc of
10301620 Nothing -> return (Nothing, outerHeight, Nothing)
@@ -1036,23 +1626,37 @@ getCoincidenceSubscribed c = do
10361626 heightRef <- liftIO $ newIORef height
10371627 innerSubdRef <- liftIO $ newIORef mInnerSubd
10381628 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
10401631 let subscribed = CoincidenceSubscribed
1041− { coincidenceSubscribedOccurrence = occRef
1632+ { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1633+ , coincidenceSubscribedOccurrence = occRef
10421634 , coincidenceSubscribedHeight = heightRef
1043− , coincidenceSubscribedSubscribers = subscribersRef
1635+ , coincidenceSubscribedSubscribers = subs
10441636 , coincidenceSubscribedOuter = subOuter
1045− , coincidenceSubscribedOuterParent = outerSubd
1637+ , coincidenceSubscribedOuterParent = outerSubscription
10461638 , coincidenceSubscribedInnerParent = innerSubdRef
1639+ , coincidenceSubscribedWeakSelf = weakSelf
10471640#ifdef DEBUG_NODEIDS
10481641 , coincidenceSubscribedNodeId = unsafeNodeId c
10491642#endif
10501643 }
1051− liftIO $ writeIORef subscribedRef subscribed
1644+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1645+ liftIO $ writeIORef subscribedRef $! subscribed
10521646 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
10541657
1055−merge :: GCompare k => DMap k Event -> Event (DMap k Identity)
1658+{-# INLINE merge #-}
1659+merge :: GCompare k => Dynamic R.PatchDMap (DMap k Event) -> Event (DMap k Identity)
10561660merge m = EventMerge $ Merge
10571661 { mergeParents = m
10581662 , mergeSubscribed = unsafeNewIORef m Nothing
@@ -1068,218 +1672,297 @@ fan e =
10681672 }
10691673 in EventSelector $ \k -> EventFan k f
10701674
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+
10711697-- | Run an event action outside of a frame
1072−runFrame :: EventM a -> IO a
1073−runFrame a = do
1698+runFrame :: EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
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
10741703 toAssignRef <- newIORef [] -- This should only actually get used when events are firing
10751704 holdInitRef <- newIORef []
1076− heightRef <- newIORef 0
1705+ mergeUpdateRef <- newIORef []
1706+ mergeInitRef <- newIORef []
1707+ heightRef <- newIORef zeroHeight
10771708 toClearRef <- newIORef []
10781709 toClearRootRef <- newIORef []
10791710 coincidenceInfosRef <- newIORef []
10801711 delayedRef <- liftIO $ newIORef IntMap.empty
1081− result <- flip runEventM (EventEnv toAssignRef holdInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) $ do
1082− result <- a
1083− let runHoldInits = do
1084− holdInits <- liftIO $ readIORef holdInitRef
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
1090− return result
1712+ let env = EventEnv spiderEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1713+ let go = do
1714+ result <- a
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
1716+ return result
1717+ result <- runEventM go env
10911718 toClear <- readIORef toClearRef
1092− forM_ toClear $ \(SomeMaybeIORef ref) -> writeIORef ref Nothing
1719+ forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
10931720 toClearRoot <- readIORef toClearRootRef
1094− forM_ toClearRoot $ \(SomeDMapIORef ref) -> writeIORef ref DMap.empty
1721+ forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
10951722 toAssign <- readIORef toAssignRef
10961723 toReconnectRef <- newIORef []
1097− forM_ toAssign $ \(SomeAssignment h v) -> do
1098− writeIORef (holdValue h) v
1099− writeIORef (holdInvalidators h) =<< invalidate toReconnectRef =<< readIORef (holdInvalidators h)
11001724 coincidenceInfos <- readIORef coincidenceInfosRef
1101− forM_ coincidenceInfos $ \(SomeCoincidenceInfo wsubInner subInner mcs) -> do
1102− touch subInner
1103− finalize wsubInner
1104− mapM_ invalidateCoincidenceHeight mcs
1725+ forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1726+ writeIORef vRef v
1727+ when debugInvalidate $ putStrLn $ "Invalidating Hold"
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"
11051754 toReconnect <- readIORef toReconnectRef
1106− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1107− wsub <- readIORef $ switchSubscribedSelfWeak subscribed
1108− finalize wsub
1755+ switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1756+ oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
11091757 wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1758+ when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
11101759 finalize wi
11111760 i <- evaluate $ switchSubscribedOwnInvalidator subscribed
11121761 wi' <- mkWeakPtrWithDebug i "wi'"
1762+ writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
11131763 writeIORef (switchSubscribedBehaviorParents subscribed) []
1114− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed))
1764+ writeIORef holdInitRef [] --TODO: Should we reuse this?
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
11151767 --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
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
1768+ sub <- newSubscriberSwitch subscribed
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
11201770 {-
1121− stackTrace <- liftIO $ liftM renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1771+ stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
11221772 liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
11231773 EventRoot _ -> "EventRoot"
11241774 EventNever -> "EventNever"
11251775 _ -> "something else"
11261776 -}
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
11281783 parentHeight <- readIORef $ eventSubscribedHeightRef subd'
11291784 myHeight <- readIORef $ switchSubscribedHeight subscribed
1130− if parentHeight == myHeight then return () else do
1131− writeIORef (switchSubscribedHeight subscribed) parentHeight
1132− mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1133− forM_ coincidenceInfos $ \(SomeCoincidenceInfo _ _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1785+ when (parentHeight /= myHeight) $ do
1786+ writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
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
1793+ forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1794+ forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> revalidateMergeHeight m
11341795 forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1135− mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1796+ height <- calculateSwitchHeight subscribed
1797+ updateSwitchHeight height subscribed
11361798 return result
11371799
1138−invalidHeight :: Int
1139−invalidHeight = -1000
1140−
1141−invalidateSubscriberHeight :: WeakSubscriber a -> IO ()
1142−invalidateSubscriberHeight ws = do
1143− ms <- deRefWeakSubscriber ws
1144− case ms of
1145− Nothing -> return () --TODO: cleanup?
1146− Just s -> case s of
1147− SubscriberPush _ subscribed -> do
1148− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1149− mapM_ invalidateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1150− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1151− SubscriberMerge _ subscribed -> do
1152− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1153− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1154− when (oldHeight /= invalidHeight) $ do
1155− writeIORef (mergeSubscribedHeight subscribed) $ invalidHeight
1156− mapM_ invalidateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1157− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1158− SubscriberFan subscribed -> do
1159− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1160− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1161− forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ invalidateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
1162− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1163− SubscriberHold _ -> return ()
1164− SubscriberSwitch subscribed -> do
1165− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1166− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1167− when (oldHeight /= invalidHeight) $ do
1168− writeIORef (switchSubscribedHeight subscribed) $ invalidHeight
1169− mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1170− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1171− SubscriberCoincidenceOuter subscribed -> do
1172− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1173− invalidateCoincidenceHeight subscribed
1174− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1175− SubscriberCoincidenceInner subscribed -> do
1176− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1177− invalidateCoincidenceHeight subscribed
1178− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1800+newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1801+
1802+{-# INLINE zeroHeight #-}
1803+zeroHeight :: Height
1804+zeroHeight = Height 0
1805+
1806+{-# INLINE invalidHeight #-}
1807+invalidHeight :: Height
1808+invalidHeight = Height (-1000)
1809+
1810+-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1811+{-# INLINE invalidHeightBeingTraversed #-}
1812+invalidHeightBeingTraversed :: Height
1813+invalidHeightBeingTraversed = Height (-1001)
1814+
1815+{-# INLINE succHeight #-}
1816+succHeight :: Height -> Height
1817+succHeight h@(Height a) =
1818+ if h == invalidHeight
1819+ then invalidHeight
1820+ else Height $ succ a
1821+
1822+invalidateSubscriberHeight :: Height -> Subscriber a -> IO ()
1823+invalidateSubscriberHeight old s = case s of
1824+ SubscriberPush _ subscribed -> do
1825+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1826+ WeakBag.traverse (pushSubscribedSubscribers subscribed) $ invalidateSubscriberHeight old
1827+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1828+ SubscriberMerge _ subscribed -> do
1829+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1830+ removeMergeHeight old subscribed
1831+ invalidateMergeHeight subscribed
1832+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1833+ SubscriberMergeChange _ -> return () -- Doesn't affect the height of the merge, because its changes are delayed
1834+ SubscriberFan subscribed -> do
1835+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1836+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1837+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
1838+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1839+ SubscriberHold _ -> return ()
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
11791895
11801896invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
11811897invalidateCoincidenceHeight subscribed = do
11821898 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
11831899 when (oldHeight /= invalidHeight) $ do
1184− writeIORef (coincidenceSubscribedHeight subscribed) $ invalidHeight
1185− mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
1186−
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
1188−
1189−recalculateSubscriberHeight :: WeakSubscriber a -> IO ()
1190−recalculateSubscriberHeight ws = do
1191− ms <- deRefWeakSubscriber ws
1192− case ms of
1193− Nothing -> return () --TODO: cleanup?
1194− Just s -> case s of
1195− SubscriberPush _ subscribed -> do
1196− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1197− mapM_ recalculateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1198− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1199− SubscriberMerge _ subscribed -> do
1200− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1201− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1202− when (oldHeight == invalidHeight) $ do
1203− height <- calculateMergeHeight subscribed
1204− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
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
1206− writeIORef (mergeSubscribedHeight subscribed) height
1207− mapM_ recalculateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1208− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1209− SubscriberFan subscribed -> do
1210− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1211− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1212− forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ recalculateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
1213− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1214− SubscriberHold _ -> return ()
1215− SubscriberSwitch subscribed -> do
1216− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1217− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1218− when (oldHeight == invalidHeight) $ do
1219− height <- calculateSwitchHeight subscribed
1220− when (height /= invalidHeight) $ do
1221− writeIORef (switchSubscribedHeight subscribed) height
1222− mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1223− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1224− SubscriberCoincidenceOuter subscribed -> do
1225− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1226− void $ recalculateCoincidenceHeight subscribed
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"
1900+ writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1901+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1902+
1903+recalculateSubscriberHeight :: Height -> Subscriber a -> IO ()
1904+recalculateSubscriberHeight new s = case s of
1905+ SubscriberPush _ subscribed -> do
1906+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1907+ WeakBag.traverse (pushSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1908+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1909+ SubscriberMerge _ subscribed -> do
1910+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1911+ addMergeHeight new subscribed
1912+ revalidateMergeHeight subscribed
1913+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1914+ SubscriberMergeChange _ -> return ()
1915+ SubscriberFan subscribed -> do
1916+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1917+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1918+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
1919+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1920+ SubscriberHold _ -> return ()
1921+ SubscriberHoldIdentity _ -> return ()
1922+ SubscriberSwitch subscribed -> do
1923+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1924+ updateSwitchHeight new subscribed
1925+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1926+ SubscriberCoincidenceOuter subscribed -> do
1927+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1928+ recalculateCoincidenceHeight subscribed
1929+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1930+ SubscriberCoincidenceInner subscribed -> do
1931+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1932+ recalculateCoincidenceHeight subscribed
1933+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1934+ SubscriberHandle -> return ()
1935+
1936+updateSwitchHeight :: Height -> SwitchSubscribed a -> IO ()
1937+updateSwitchHeight new subscribed = do
1938+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1939+ when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1940+ when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1941+ writeIORef (switchSubscribedHeight subscribed) $! new
1942+ WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
12321943
12331944recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
12341945recalculateCoincidenceHeight subscribed = do
12351946 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1236− when (oldHeight == invalidHeight) $ do
1947+ when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
12371948 height <- calculateCoincidenceHeight subscribed
12381949 when (height /= invalidHeight) $ do
1239− writeIORef (coincidenceSubscribedHeight subscribed) height
1240− mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed) --TODO: This should probably be mandatory, just like with the merge and switch ones
1950+ writeIORef (coincidenceSubscribedHeight subscribed) $! height
1951+ WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
12411952
1242−calculateMergeHeight :: MergeSubscribed k -> IO Int
1243−calculateMergeHeight subscribed = if DMap.null (mergeSubscribedParents subscribed) then return 0 else do
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'
1953+getEventSubscribedHeight :: EventSubscribed a -> IO Height
1954+getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
12471955
1248−calculateSwitchHeight :: SwitchSubscribed a -> IO Int
1249−calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef =<< readIORef (switchSubscribedCurrentParent subscribed)
1956+calculateSwitchHeight :: SwitchSubscribed a -> IO Height
1957+calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
12501958
1251−calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Int
1959+calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Height
12521960calculateCoincidenceHeight subscribed = do
1253− outerHeight <- readIORef $ eventSubscribedHeightRef $ coincidenceSubscribedOuterParent subscribed
1254− innerHeight <- maybe (return 0) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1961+ outerHeight <- readIORef $ eventSubscribedHeightRef $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1962+ innerHeight <- maybe (return zeroHeight) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
12551963 return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
12561964
1257−{-
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
1965+data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed a)
12831966
12841967invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
12851968invalidate toReconnectRef wis = do
@@ -1287,19 +1970,19 @@ invalidate toReconnectRef wis = do
12871970 mi <- deRefWeak wi
12881971 case mi of
12891972 Nothing -> do
1290− when debugInvalidate $ liftIO $ putStrLn "invalidate Dead"
1973+ traceInvalidate "invalidate Dead"
12911974 return () --TODO: Should we clean this up here?
12921975 Just i -> do
12931976 finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
12941977 case i of
12951978 InvalidatorPull p -> do
1296− when debugInvalidate $ liftIO $ putStrLn "invalidate Pull"
1979+ traceInvalidate $ "invalidate: Pull" <> showNodeId p
12971980 mVal <- readIORef $ pullValue p
12981981 forM_ mVal $ \val -> do
12991982 writeIORef (pullValue p) Nothing
1300− writeIORef (pullSubscribedInvalidators val) =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1983+ writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
13011984 InvalidatorSwitch subscribed -> do
1302− when debugInvalidate $ liftIO $ putStrLn "invalidate Switch"
1985+ traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
13031986 modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
13041987 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
13051988
@@ -1307,83 +1990,228 @@ invalidate toReconnectRef wis = do
13071990-- Reflex integration
13081991--------------------------------------------------------------------------------
13091992
1310−data Spider
1311−
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
1993+-- | The default, global Spider environment
1994+type Spider = SpiderEnv Global
13271995
1328−instance R.MonadSample Spider SpiderHost where
1329− {-# INLINE sample #-}
1330− sample = SpiderHost . readBehavior . unSpiderBehavior
1996+-- | Designates the default, global Spider timeline
1997+data Global
13311998
1332−instance R.MonadHold Spider SpiderHost where
1333− hold v0 = SpiderHost . liftM SpiderBehavior . runFrame . hold v0 . unSpiderEvent
1999+{-# NOINLINE globalSpiderEnv #-}
2000+globalSpiderEnv :: SpiderEnv Global
2001+globalSpiderEnv = unsafePerformIO unsafeNewSpiderEnv
13342002
1335−instance R.MonadSample Spider BehaviorM where
1336− {-# INLINE sample #-}
1337− sample = readBehaviorTracked . unSpiderBehavior
2003+-- | Stores all global data relevant to a particular Spider timeline; only one
2004+-- value should exist for each type @x@
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+ }
13382036
1339−instance R.MonadSample Spider EventM where
1340− {-# INLINE sample #-}
1341− sample = liftIO . readBehavior . unSpiderBehavior
2037+-- | Create a new SpiderEnv
2038+newSpiderEnv :: IO (Some SpiderEnv)
2039+newSpiderEnv = Some.This <$> unsafeNewSpiderEnv
13422040
1343−instance R.MonadHold Spider EventM where
1344− {-# INLINE hold #-}
1345− hold v0 e = SpiderBehavior <$> hold v0 (unSpiderEvent e)
2041+newtype SpiderPullM x a = SpiderPullM (BehaviorM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
13462042
1347−data RootTrigger a = forall k. GCompare k => RootTrigger (IORef [WeakSubscriber a], IORef (DMap k Identity), k a)
1348−newtype SpiderEventHandle a = SpiderEventHandle { unEventHandle :: Event a }
2043+newtype SpiderPushM x a = SpiderPushM (ComputeM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
13492044
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 #-}
13512141 subscribeEvent e = SpiderHostFrame $ do
1352− _ <- getEventSubscribed $ unSpiderEvent e --TODO: The result of this should actually be used
1353− return $ SpiderEventHandle (unSpiderEvent e)
1354−
1355−instance R.ReflexHost Spider where
1356− type EventTrigger Spider = RootTrigger
1357− type EventHandle Spider = SpiderEventHandle
1358− type HostFrame Spider = SpiderHostFrame
1359−
1360−instance R.MonadReadEvent Spider ReadPhase where
1361− {-# INLINE readEvent #-}
1362− readEvent = ReadPhase . liftM (fmap return) . readEvent . unEventHandle
1363−
1364−instance MonadRef EventM where
1365− type Ref EventM = Ref IO
1366− {-# INLINE newRef #-}
1367− {-# INLINE readRef #-}
1368− {-# INLINE writeRef #-}
2142+ subscription <- subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2143+ return $ SpiderEventHandle subscription
2144+
2145+instance R.ReflexHost (SpiderEnv x) where
2146+ type EventTrigger (SpiderEnv x) = RootTrigger
2147+ type EventHandle (SpiderEnv x) = SpiderEventHandle
2148+ type HostFrame (SpiderEnv x) = SpiderHostFrame x
2149+
2150+instance R.MonadReadEvent (SpiderEnv x) (ReadPhase x) where
2151+ {-# INLINABLE readEvent #-}
2152+ readEvent h = ReadPhase $ fmap (fmap return) $ liftIO $ do
2153+ let EventSubscription sln subscribed = unSpiderEventHandle h
2154+ result <- readEventSubscribed subscribed
2155+ touch sln
2156+ return result
2157+
2158+instance MonadRef (EventM x) where
2159+ type Ref (EventM x) = Ref IO
2160+ {-# INLINABLE newRef #-}
2161+ {-# INLINABLE readRef #-}
2162+ {-# INLINABLE writeRef #-}
13692163 newRef = liftIO . newRef
13702164 readRef = liftIO . readRef
13712165 writeRef r a = liftIO $ writeRef r a
13722166
1373−instance MonadAtomicRef EventM where
1374− {-# INLINE atomicModifyRef #-}
2167+instance MonadAtomicRef (EventM x) where
2168+ {-# INLINABLE atomicModifyRef #-}
13752169 atomicModifyRef r f = liftIO $ atomicModifyRef r f
13762170
1377−newtype SpiderHost a = SpiderHost { runSpiderHost :: IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
1378−
1379−newtype SpiderHostFrame a = SpiderHostFrame { runSpiderHostFrame :: EventM a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
1380−
1381−instance R.MonadSample Spider SpiderHostFrame where
1382− sample = SpiderHostFrame . R.sample --TODO: This can cause problems with laziness, so we should get rid of it if we can
1383−
1384−instance R.MonadHold Spider SpiderHostFrame where
1385− {-# INLINE hold #-}
1386− hold v0 e = SpiderHostFrame $ R.hold v0 e
2171+-- | The monad for actions that manipulate a Spider timeline identified by @x@
2172+newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderEnv x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2173+
2174+instance Monad (SpiderHost x) where
2175+ {-# INLINABLE (>>=) #-}
2176+ SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
2177+ {-# INLINABLE (>>) #-}
2178+ SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
2179+ {-# INLINABLE return #-}
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
13872215
13882216newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
13892217newEventWithTriggerIO f = do
@@ -1401,44 +2229,60 @@ newFanEventWithTriggerIO f = do
14012229 }
14022230 return $ EventSelector $ \k -> EventRoot k r
14032231
1404−instance R.MonadReflexCreateTrigger Spider SpiderHost where
1405− newEventWithTrigger = SpiderHost . liftM SpiderEvent . newEventWithTriggerIO
1406− newFanEventWithTrigger f = SpiderHost $ do
2232+instance R.MonadReflexCreateTrigger (SpiderEnv x) (SpiderHost x) where
2233+ newEventWithTrigger = SpiderHost . lift . fmap SpiderEvent . newEventWithTriggerIO
2234+ newFanEventWithTrigger f = SpiderHost $ lift $ do
14072235 es <- newFanEventWithTriggerIO f
14082236 return $ R.EventSelector $ SpiderEvent . select es
14092237
1410−instance R.MonadReflexCreateTrigger Spider SpiderHostFrame where
1411− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . liftM SpiderEvent . newEventWithTriggerIO
2238+instance R.MonadReflexCreateTrigger (SpiderEnv x) (SpiderHostFrame x) where
2239+ newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
14122240 newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
14132241 es <- newFanEventWithTriggerIO f
14142242 return $ R.EventSelector $ SpiderEvent . select es
14152243
1416−instance R.MonadSubscribeEvent Spider SpiderHost where
1417− subscribeEvent e = SpiderHost $ do
1418− _ <- runFrame $ getEventSubscribed $ unSpiderEvent e --TODO: The result of this should actually be used
1419− return $ SpiderEventHandle (unSpiderEvent e)
2244+instance R.MonadSubscribeEvent (SpiderEnv x) (SpiderHost x) where
2245+ {-# INLINABLE subscribeEvent #-}
2246+ subscribeEvent = subscribeEventSpiderHost
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
14202258
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
14222266
1423−instance R.MonadReflexHost Spider SpiderHost where
1424− type ReadPhase SpiderHost = ReadPhase
1425− fireEventsAndRead es (ReadPhase a) = SpiderHost $ run es a
1426− runHostFrame = SpiderHost . runFrame . runSpiderHostFrame
2267+instance R.MonadReflexHost (SpiderEnv x) (SpiderHost x) where
2268+ type ReadPhase (SpiderHost x) = ReadPhase x
2269+ fireEventsAndRead es (ReadPhase a) = run es a
2270+ runHostFrame = runFrame . runSpiderHostFrame
14272271
1428−instance MonadRef SpiderHost where
1429− type Ref SpiderHost = Ref IO
2272+instance MonadRef (SpiderHost x) where
2273+ type Ref (SpiderHost x) = Ref IO
14302274 newRef = SpiderHost . newRef
14312275 readRef = SpiderHost . readRef
14322276 writeRef r = SpiderHost . writeRef r
14332277
1434−instance MonadAtomicRef SpiderHost where
2278+instance MonadAtomicRef (SpiderHost x) where
14352279 atomicModifyRef r = SpiderHost . atomicModifyRef r
14362280
1437−instance MonadRef SpiderHostFrame where
1438− type Ref SpiderHostFrame = Ref IO
2281+instance MonadRef (SpiderHostFrame x) where
2282+ type Ref (SpiderHostFrame x) = Ref IO
14392283 newRef = SpiderHostFrame . newRef
14402284 readRef = SpiderHostFrame . readRef
14412285 writeRef r = SpiderHostFrame . writeRef r
14422286
1443−instance MonadAtomicRef SpiderHostFrame where
2287+instance MonadAtomicRef (SpiderHostFrame x) where
14442288 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 #-}
211
312module Reflex.Bench.Focused where
413
514import Reflex
615import Reflex.TestPlan
716
8−import Control.Monad.Fix
17+import Control.Applicative
918import Control.Monad
19+import Control.Monad.Fix
1020import Control.Monad.Identity
11−import Control.Applicative
12−--import Data.Foldable
13−import Data.Traversable (for)
21+import Data.Traversable (for, traverse)
1422
15−import Data.Maybe
23+import qualified Data.Dependent.Map as DMap
1624import Data.Map (Map)
1725import qualified Data.Map as Map
18−import qualified Data.Dependent.Map as DMap
26+import Data.Maybe
1927
2028import Data.Functor.Misc
2129
22−import Data.Tuple
2330import Data.List
2431import Data.List.Split
32+import Data.Tuple
2533
26−import Data.Word
2734import Data.Monoid
35+import Data.Word
2836
37+import Control.DeepSeq
2938
30−import Prelude hiding (sum, concat)
39+import Prelude hiding (concat, sum)
3140
3241
3342mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
@@ -38,15 +47,15 @@ mergeTree n es | length es <= n = sum' es
3847 subTrees = map sum' merges
3948 sum' = fmap sum . mconcat . fmap (fmap (:[]) )
4049
41−mergeListDyn :: (Reflex t, MonadHold t m) => [Dynamic t a] -> m (Dynamic t [a])
42−mergeListDyn dyns = traverse (mapDyn pure) dyns >>= mconcatDyn
50+mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
51+mergeListDyn = mconcat . fmap (fmap pure)
4352
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
4554mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
46− | otherwise = traverse sumDyns merges >>= mergeTreeDyn n
55+ | otherwise = mergeTreeDyn n $ fmap sumDyns merges
4756 where
48− merges = chunksOf n dyns
49− sumDyns ds = mergeListDyn ds >>= mapDyn sum
57+ merges = chunksOf n dyns
58+ sumDyns = fmap sum . mergeListDyn
5059
5160
5261
@@ -93,7 +102,7 @@ fanMerge n e = leftmost $ s <$> [0..n - 1] where
93102-- Dumb version of the above using simple fmapMaybe to filter
94103fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
95104fmapFanMerge 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
97106 then Just k
98107 else Nothing
99108
@@ -116,13 +125,13 @@ fmapChain :: (Functor f) => Word -> f Word -> f Word
116125fmapChain = iter (fmap (+1))
117126
118127mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
119−mapDynChain = iterM (mapDyn (+1))
128+mapDynChain = iterM (return . fmap (+1))
120129
121130joinDynChain :: (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)
123132
124133combineDynChain :: (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)
126135
127136pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
128137pingPong e1 e2 = do
@@ -170,7 +179,7 @@ switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
170179-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
171180
172181switchMergeBehaviors :: 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
174183 where joinMap m = traverse sample =<< sample m
175184
176185-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
@@ -244,7 +253,7 @@ subscribing n frames =
244253 ]
245254
246255 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)
248257 testSub name test = testE name (switches frames test)
249258
250259-- Set of benchmark to test specifically merging dynamic collections
@@ -300,9 +309,9 @@ dynamics n =
300309 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
301310 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
302311 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
303− , testE "dense mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< dense
304− , testE "sparse mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< sparse
305− , testE "mergeDyn" $ fmap updated $ mapDyn sum =<< mergeListDyn =<< sparse
312+ , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313+ , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
314+ , testE "mergeDyn" $ fmap (updated . fmap sum . mergeListDyn) sparse
306315 ] where
307316
308317 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 #-}
28
39module Reflex.Plan.Pure where
410
@@ -14,9 +20,9 @@ import qualified Data.IntMap as IntMap
1420import Data.IntSet (IntSet)
1521import qualified Data.IntSet as IntSet
1622
17−import Data.Monoid
1823import Data.Bifunctor
1924import Data.Maybe
25+import Data.Monoid
2026import Prelude
2127
2228
@@ -31,6 +37,8 @@ liftPlan = PurePlan . lift
3137
3238instance MonadHold (Pure Int) PurePlan where
3339 hold initial = liftPlan . hold initial
40+ holdDyn initial = liftPlan . holdDyn initial
41+ holdIncremental initial = liftPlan . holdIncremental initial
3442
3543instance MonadSample (Pure Int) PurePlan where
3644 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 #-}
211module Reflex.Plan.Reflex
312 ( TestPlan(..)
413 , runPlan
@@ -9,6 +18,7 @@ module Reflex.Plan.Reflex
918
1019
1120 , readSchedule
21+ , readSchedule_
1222 , testSchedule
1323 , readEvent'
1424 , makeDense
@@ -27,13 +37,15 @@ import Control.Monad
2737import Control.Monad.Identity
2838import Control.Monad.State.Strict
2939
40+import Control.Monad.Ref
3041import Data.Dependent.Sum (DSum (..))
31−import Data.Monoid
32−import Data.Traversable (traverse, sequenceA)
33−import Data.Maybe
3442import 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)
3646
47+import Control.DeepSeq
48+import Control.Exception
3749import Data.IntMap.Strict (IntMap)
3850import Data.IORef
3951import System.Mem
@@ -75,7 +87,7 @@ instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => T
7587firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
7688firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
7789
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)
7991runPlan (Plan p) = runHostFrame $ runStateT p mempty
8092
8193
@@ -90,29 +102,31 @@ makeDense s = fromMaybe (emptyRange 0) $ do
90102-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
91103-- after the last event)
92104-- 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)
94106testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
95107
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)
97109readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
98110
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
100115triggerFrame readResult _ occs = do
101116 triggers <- catMaybes <$> traverse firingTrigger occs
102− fireEventsAndRead triggers readResult
103−
117+ liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
104118
105119readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
106120readEvent' = readEvent >=> sequenceA
107121
108122
109123-- 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)
111125runTestB p = do
112126 (b, s) <- runPlan p
113127 testSchedule s $ sample b
114128
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))
116130runTestE p = do
117131 (e, s) <- runPlan p
118132 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
2−to serve as a reference for the semantics of the Reflex class. All implementations
3−of Reflex should produce the same results as this implementation, although performance
4−and laziness/strictness may differ.
5−-}
1+-- | This module provides a pure implementation of Reflex, which is intended to
2+-- serve as a reference for the semantics of the Reflex class. All
3+-- implementations of Reflex should produce the same results as this
4+-- implementation, although performance and laziness/strictness may differ.
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 #-}
617
7−{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, EmptyDataDecls, InstanceSigs #-}
818module Reflex.Pure where
919
10−import Reflex.Class
11−import Data.Functor.Misc
12−import Data.Functor.Identity
13−import Data.MemoTrie
20+import Control.Applicative -- Necessary for GHC 7.8
21+import Control.Monad
1422import Data.Dependent.Map (DMap, GCompare)
1523import 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
1629
1730data Pure t
1831
1932-- | 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
21−-- to memoize functions of t, not for any of its other capabilities.
33+-- any y :: t such that pred x < y < x. The HasTrie instance will be used
34+-- exclusively to memoize functions of t, not for any of its other capabilities.
2235instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
2336
2437 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)) }
2641
2742 type PushM (Pure t) = (->) t
2843 type PullM (Pure t) = (->) t
@@ -39,9 +54,14 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
3954 pull :: PullM (Pure t) a -> Behavior (Pure t) a
4055 pull = Behavior . memo
4156
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)
4363 merge events = Event $ memo $ \t ->
44− let currentOccurrences = unwrapDMapMaybe (($ t) . unEvent) events
64+ let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
4565 in if DMap.null currentOccurrences
4666 then Nothing
4767 else Just currentOccurrences
@@ -55,21 +75,90 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
5575 coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
5676 coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
5777
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
59130
60131 sample :: Behavior (Pure t) a -> (t -> a)
61132 sample = unBehavior
62133
63134instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
64135
65− hold :: a -> Event (Pure t) a -> (t -> (Behavior (Pure t) a))
136+ hold :: a -> Event (Pure t) a -> t -> Behavior (Pure t) a
66137 hold initialValue e initialTime = Behavior f
67138 where f = memo $ \sampleTime ->
68139 -- 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.
70158 if sampleTime <= initialTime
71159 then initialValue
72160 else let lastTime = pred sampleTime
161+ lastValue = f lastTime
73162 in case unEvent e lastTime of
74− Nothing -> f lastTime
75− Just x -> x
163+ Nothing -> lastValue
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 #-}
27
38module Reflex.Test
49 ( 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 #-}
215module Reflex.Test.CrossImpl (test) where
316
4−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl, and)
17+import Prelude hiding (and, foldl, mapM, mapM_, sequence, sequence_)
518
19+import Control.Monad.Ref
620import Reflex.Class
7−import Reflex.Host.Class
821import Reflex.Dynamic
9−import qualified Reflex.Spider.Internal as S
22+import Reflex.Host.Class
1023import qualified Reflex.Pure as P
11−import Control.Monad.Ref
24+import qualified Reflex.Spider.Internal as S
1225
13−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
14−import qualified Data.Set as Set
26+import Control.Arrow (second, (&&&))
27+import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28+import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29+import Control.Monad.Writer hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
30+import Data.Dependent.Map (DSum (..))
31+import Data.Foldable
1532import Data.Map.Strict (Map)
1633import qualified Data.Map.Strict as Map
17−import Control.Arrow (second, (&&&))
34+import qualified Data.Set as Set
1835import 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
2436import System.Exit
37+import System.Mem
2538
2639import System.IO.Unsafe
2740
@@ -73,7 +86,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
7386 outputs <- forM times $ \t -> do
7487 forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
7588 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
7790 mret <- readRef reTrigger
7891 let firing = case mret of
7992 Just ret -> [ret :=> Identity val]
@@ -81,7 +94,7 @@ testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
8194 fireEventsAndRead firing $ sequence =<< readEvent e'Handle
8295 liftIO performGC
8396 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)
8598
8699tracePerf :: Show a => a -> b -> b
87100tracePerf = flip const
@@ -111,7 +124,7 @@ testCases =
111124 b' <- hold "123" e
112125 return (b', e)
113126 , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
114− e' <- liftM updated $ count e
127+ e' <- updated <$> count e
115128 b' <- hold (0 :: Int) e'
116129 return (b', e')
117130 , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -124,23 +137,19 @@ testCases =
124137 return (b, e'')
125138 , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
126139 let e' = flip pushAlways e $ const $ do
127− let ea = fmap (const "a") e
128− let eb = fmap (const "b") e
129− let eab = leftmost [ea, eb]
130− liftM switch $ hold eab never
140+ let eab = splitRecombineEvent e
141+ switch <$> hold eab never
131142 e'' = coincidence e'
132143 return (b, e'')
133144 , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
134145 let e' = flip pushAlways e $ const $ do
135− let ea = fmap (const "a") e
136− let eb = fmap (const "b") e
137− let eab = leftmost [ea, eb]
138− liftM switch $ hold eab (fmap (const e) e)
146+ let eab = splitRecombineEvent e
147+ switch <$> hold eab (fmap (const e) e)
139148 e'' = coincidence e'
140149 return (b, e'')
141150 , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
142151 let e' = leftmost [e, e]
143− e'' <- liftM switch $ hold e' (fmap (const e) e)
152+ e'' <- switch <$> hold e' (fmap (const e) e)
144153 return (b, e'')
145154 , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
146155 let e' = fmap (const e) e
@@ -160,11 +169,11 @@ testCases =
160169 return (b, e'')
161170 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
162171 let e' = leftmost [e, e]
163− e'' <- liftM switch $ hold never (fmap (const e') e)
172+ e'' <- switch <$> hold never (fmap (const e') e)
164173 return (b, e'')
165174 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
166175 let e' = flip push e $ \_ -> do
167− return . Just =<< onceE e
176+ Just <$> onceE e
168177 e'' <- switchPromptly never e'
169178 return (b, e'')
170179 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -173,7 +182,7 @@ testCases =
173182 e'' <- switchPromptly never e'
174183 return (b, e'')
175184 , (,) "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
177186 e'' = coincidence e'
178187 return (b, e'')
179188 , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -212,12 +221,19 @@ testCases =
212221 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
213222 bb <- hold "b" e
214223 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
216225 let eInner = switch bd
217226 e' = leftmost [eOuter, eInner]
218227 return (b, e')
219228 ]
220229
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+
221237test :: IO ()
222238test = do
223239 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 #-}
27
38module Reflex.Test.Micro (testCases) where
49
510import Reflex
611import Reflex.TestPlan
712
8−import Data.Word
9−import Control.Monad
1013import Control.Applicative
14+import Control.Monad
1115import Data.Char
12−import Data.Monoid
1316import Data.Functor.Misc
17+import Data.Monoid
1418
1519import Data.Foldable
1620
17−import Data.Map (Map)
1821import qualified Data.Map as Map
1922
2023import Prelude
@@ -22,32 +25,30 @@ import Prelude
2225testCases :: [(String, TestCase)]
2326testCases =
2427 [ testB "hold" $ hold "0" =<< events1
28+
2529 , testB "count" $ do
26− b <- current <$> (count =<< events2)
27− return $ (+ (0::Int)) <$> b
30+ b <- current <$> (count =<< events2)
31+ return $ (+ (0::Int)) <$> b
2832
2933 , testB "pull-1" $ do
3034 b <- hold "0" =<< events1
31− return (id <$> id <$> b)
32−
35+ return (pull $ sample $ pull $ sample b)
3336
3437 , testB "pull-2" $ do
3538 b1 <- behavior1
36− return (id <$> pull $ liftA2 (<>) (sample b1) (sample b1))
39+ return (pull $ liftA2 (<>) (sample b1) (sample b1))
3740
3841 , testB "pull-3" $ do
3942 b1 <- behavior1
4043 b2 <- behavior2
41− return (id <$> pull $ liftA2 (<>) (sample b1) (sample b2))
44+ return (pull $ liftA2 (<>) (sample b1) (sample b2))
4245
4346 , testB "pull-4" $ do
44− es <- planList ["a", "b", "c"]
45− e <- plan [(0, ())]
46−
47− b <- hold (constant "") $
48− pushAlways (const $ hold "z" es) e
49−
50− return (join b)
47+ es <- planList ["a", "b", "c"]
48+ e <- plan [(0, ())]
49+ b <- hold (constant "") $
50+ pushAlways (const $ hold "z" es) e
51+ return (join b)
5152
5253 , testE "tag-1" $ do
5354 b1 <- behavior1
@@ -66,9 +67,9 @@ testCases =
6667
6768 , testE "leftmost" $ liftA2 leftmost2 events1 events2
6869
69− , testE "appendEvents-1" $ liftA2 appendEvents events1 events2
70− , testE "appendEvents-2" $ liftA2 appendEvents events3 events2
70+ , testE "appendEvents-1" $ liftA2 mappend events1 events2
7171
72+ , testE "appendEvents-2" $ liftA2 mappend events3 events2
7273
7374 , testE "merge-1" $ do
7475 e <- events1
@@ -77,20 +78,17 @@ testCases =
7778 , testE "merge-2" $ do
7879 e <- events1
7980 let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
80−
8181 let ee = flip pushAlways e $ const $ return m
8282 return $ coincidence ee
8383
84− , testE "onceE-1" $ do
84+ , testE "headE-1" $ do
8585 e <- events1
86− onceE $ leftmost [e, e]
86+ headE $ leftmost [e, e]
8787
88−
89− , testE "onceE-2" $ do
88+ , testE "headE-2" $ do
9089 e <- events1
9190 b <- hold never (e <$ e)
92− onceE $ switch b
93−
91+ headE $ switch b
9492
9593 , testE "switch-1" $ do
9694 e <- events1
@@ -111,7 +109,6 @@ testCases =
111109 e <- events1
112110 switch <$> hold (deep e) (e <$ e)
113111
114−
115112 , testE "switch-5" $ do
116113 e <- events1
117114 return $ coincidence $ flip pushAlways e $ const $
@@ -122,7 +119,6 @@ testCases =
122119 return $ coincidence $ flip pushAlways e $ const $ do
123120 switch <$> hold ("x" <$ e) (e <$ e)
124121
125−
126122 , testE "switchPromptly-1" $ do
127123 e <- events1
128124 let e' = e <$ e
@@ -147,7 +143,7 @@ testCases =
147143 , testE "switchPromptly-5" $ do
148144 e <- events1
149145 switchPromptly never $ flip push e $
150− const (Just <$> onceE e)
146+ const (Just <$> headE e)
151147
152148 , testE "switchPromptly-6" $ do
153149 e <- events1
@@ -157,7 +153,7 @@ testCases =
157153 , testE "coincidence-1" $ do
158154 e <- events1
159155 return $ coincidence $ flip pushAlways e $
160− const $ return (id <$> e)
156+ const $ return e
161157
162158 , testE "coincidence-2" $ do
163159 e <- events1
@@ -172,7 +168,7 @@ testCases =
172168 , testE "coincidence-4" $ do
173169 e <- events1
174170 return $ coincidence $ flip pushAlways e $
175− const (onceE e)
171+ const (headE e)
176172
177173 , testE "coincidence-5" $ do
178174 e <- events1
@@ -192,16 +188,16 @@ testCases =
192188
193189 , testB "holdWhileFiring" $ do
194190 e <- events1
195− eo <- onceE e
191+ eo <- headE e
196192 bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
197193 return $ pull $ sample =<< sample bb
198194
199195 , testE "joinDyn" $ do
200196 e <- events1
201197 bb <- hold "b" e
202− bd <- hold never . fmap (const e) =<< onceE e
198+ bd <- hold never . fmap (const e) =<< headE e
203199
204− eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
200+ eOuter <- pushAlways sample . fmap (const bb) <$> headE e
205201 let eInner = switch bd
206202 return $ leftmost [eOuter, eInner]
207203
@@ -211,13 +207,13 @@ testCases =
211207
212208 , testB "mapDyn" $ do
213209 d <- foldDyn (++) "0" =<< events1
214− current <$> mapDyn (map toUpper) d
210+ return $ current $ fmap (map toUpper) d
215211
216212 , testB "combineDyn" $ do
217213 d1 <- foldDyn (++) "0" =<< events1
218− d2 <- mapDyn (map toUpper) =<< foldDyn (++) "0" =<< events2
214+ d2 <- fmap (fmap (map toUpper)) $ foldDyn (++) "0" =<< events2
219215
220− current <$> combineDyn (<>) d1 d2
216+ return $ current $ zipDynWith (<>) d1 d2
221217 , testE "fan-1" $ do
222218 e <- fmap toMap <$> events1
223219 let es = select (fanMap e) . Const2 <$> values
@@ -231,7 +227,7 @@ testCases =
231227 return (mergeList es)
232228
233229 , testE "fan-3" $ do
234− f <- fanMap <$> fmap toMap <$> events3
230+ f <- fanMap . fmap toMap <$> events3
235231 return $ select f (Const2 'c')
236232
237233 , testE "fan-4" $ do
@@ -243,52 +239,20 @@ testCases =
243239 return $ toUpper <$> select (fanMap e) (Const2 'c')
244240
245241 , testE "fan-6" $ do
246− f <- fanMap <$> fmap toMap <$> events1
242+ f <- fanMap . fmap toMap <$> events1
247243 return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
248244
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−
263245 ] where
264246
265247 events1, events2, events3 :: TestPlan t m => m (Event t String)
266248 events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
267249 events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
268250
269− events3 = liftA2 appendEvents 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−
251+ events3 = liftA2 mappend events1 events2
287252
288253 values = "abcde"
289254 toMap str = Map.fromList $ map (\c -> (c, c)) str
290255
291−
292256 behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
293257 behavior1 = hold "1" =<< events1
294258 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 #-}
27
38module Main (main) where
49
510import Reflex.Test
611
7−import qualified Reflex.Bench.Focused as Focused
8−import qualified Reflex.Test.Micro as Micro
9−import Data.Functor
1012import Data.Bifunctor
13+import Data.Functor
1114import Data.List
15+import qualified Reflex.Bench.Focused as Focused
16+import qualified Reflex.Test.Micro as Micro
1217
13−import System.Exit
1418import System.Environment
19+import System.Exit
1520
1621import Prelude
1722
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 #-}
29module Reflex.TestPlan
310 ( TestPlan(..)
411
@@ -10,9 +17,10 @@ module Reflex.TestPlan
1017
1118 ) where
1219
13−import Reflex.Class
20+import Control.DeepSeq
1421import Control.Monad.Fix
1522import Data.Word
23+import Reflex.Class
1624
1725
1826import Prelude
@@ -32,13 +40,13 @@ type TestE a = forall t m. TestPlan t m => m (Event t a)
3240type TestB a = forall t m. TestPlan t m => m (Behavior t a)
3341
3442data TestCase where
35− TestE :: (Show a, Eq a) => TestE a -> TestCase
36− TestB :: (Show a, Eq a) => TestB a -> TestCase
43+ TestE :: (Show a, Eq a, NFData a) => TestE a -> TestCase
44+ TestB :: (Show a, Eq a, NFData a) => TestB a -> TestCase
3745
3846-- 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)
4048testE name test = (name, TestE test)
4149
42−testB :: (Eq a, Show a) => String -> TestB a -> (String, TestCase)
50+testB :: (Eq a, Show a, NFData a) => String -> TestB a -> (String, TestCase)
4351testB name test = (name, TestB test)
4452
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