on develop help sign in
Merge branch 'develop'
66e001cRyan Trinkle
Quickref.mdnew file+171
@@ -0,0 +1,171 @@
1+# Reflex Quick(ish) Reference
2+
3+## Typeclasses
4+
5+Many Reflex functions operate in monadic context `m a`, where the monad 'm' supports various additional typeclasses such as MonadWidget, MonadHold, or MonadSample in addition to Monad itself. The actual 'm' in use will be determined by the top-level entry point of the FRP host (such as Reflex-Dom -- see the bottom of the Reflex-Dom quick reference for details).
6+
7+The function signatures here have been simplified by removing many typeclass constraints and adding simple annotations to each function. Also the ubiquitous 't' type parameter has been removed.
8+
9+Some of these functions are *pure*: They operate on Events, Behaviors, or Dynamics uniformly without regard to the "current time". Other functions must operate in some monadic context, because they produce a result "as of now" (e.g., any function that takes an "initial" value, or returns a "current" value). Annotations are used to distinguish these cases:
10+
11+```haskell
12+[ ] -- Pure function
13+[S] -- Function runs in any monad supporting MonadSample
14+[H] -- Function runs in any monad supporting MonadHold
15+```
16+
17+Since MonadHold depends on MonadSample, any [S] function also runs in [H] context.
18+
19+## Functions producing Event
20+
21+```haskell
22+-- Trivial Event
23+[ ] never :: Event a
24+
25+-- Extract Event from Dynamic
26+[ ] updated :: Dynamic a -> Event a
27+
28+-- Transform Event to Event using function
29+[ ] fmap :: (a -> b) -> Event a -> Event b
30+[ ] fmapMaybe :: (a -> Maybe b) -> Event a -> Event b
31+[ ] ffilter :: (a -> Bool) -> Event a -> Event a
32+[ ] ffor :: Event a -> (a -> b) -> Event b
33+[ ] fforMaybe :: Event a -> (a -> Maybe b) -> Event b
34+
35+-- Event to identical Event with debug trace. (Only prints if Event is ultimately used.)
36+[ ] traceEvent :: Show a => String -> Event a -> Event a
37+[ ] traceEventWith :: (a -> String) -> Event a -> Event a
38+
39+-- Transform Event to Event by sampling Behavior or Dynamic
40+[ ] gate :: Behavior Bool -> Event a -> Event a
41+[ ] tag :: Behavior a -> Event b -> Event a
42+[ ] tagDyn :: Dynamic a -> Event b -> Event a
43+[ ] attach :: Behavior a -> Event b -> Event (a, b)
44+[ ] attachDyn :: Dynamic a -> Event b -> Event (a, b)
45+[ ] attachWith :: (a -> b -> c) -> Behavior a -> Event b -> Event c
46+[ ] attachDynWith :: (a -> b -> c) -> Dynamic a -> Event b -> Event c
47+[ ] attachWithMaybe :: (a -> b -> Maybe c) -> Behavior a -> Event b -> Event c
48+[ ] attachDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
49+
50+-- Combine multiple Events
51+[ ] <> :: Monoid a => Event a -> Event a -> Event a
52+[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
53+[ ] leftmost :: [Event a] -> Event a
54+[ ] mergeList :: [Event a] -> Event (NonEmpty a)
55+[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
56+[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
57+
58+-- Efficient one-to-many fanout
59+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
60+[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
61+[ ] select :: EventSelector k -> k a -> Event a
62+
63+-- Event to Event via function that can sample current values
64+[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
65+[ ] pushAlways :: (a -> m b ) -> Event a -> Event b
66+ -- Note supplied function operates in [H] context
67+
68+-- Split Event into next occurrence and all other future occurrences, as of now.
69+[H] headE :: Event a -> m (Event a)
70+[H] tailE :: Event a -> m (Event a)
71+[H] headTailE :: Event a -> m (Event a, Event a)
72+```
73+
74+## Functions producing Behavior
75+
76+```haskell
77+-- Trivial Behavior
78+[ ] constant :: a -> Behavior a
79+
80+-- Extract Behavior from Dynamic
81+[ ] current :: Dynamic a -> Behavior a
82+
83+-- Create Behavior with given initial value, updated when the Event fires.
84+[H] hold :: a -> Event a -> m (Behavior a)
85+
86+-- Transform Behavior to Behavior using function
87+[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
88+[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
89+[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
90+[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
91+[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
92+-- ... plus many more due to typeclass membership
93+
94+-- Behavior to Behavior by sampling current values
95+[S] sample :: Behavior a -> m a
96+[ ] pull :: m a -> Behavior a
97+ -- Note supplied value is in [S] context
98+```
99+
100+## Functions producing Dynamic
101+
102+```haskell
103+-- Trivial Dynamic
104+[ ] constDyn :: a -> Dynamic a
105+
106+-- Create Dynamic with given initial value, updated (various ways) when the Event fires.
107+[H] holdDyn :: a -> Event a -> m (Dynamic a)
108+[H] foldDyn :: (a -> b -> b ) -> b -> Event a -> m (Dynamic b)
109+[H] foldDynMaybe :: (a -> b -> Maybe b ) -> b -> Event a -> m (Dynamic b)
110+[H] foldDynM :: (a -> b -> m' b ) -> b -> Event a -> m (Dynamic b)
111+[H] foldDynMaybeM :: (a -> b -> m' (Maybe b)) -> b -> Event a -> m (Dynamic b)
112+ -- Note m' supplies [H] context
113+
114+-- Initial value is 0; counts Event firings from now.
115+[H] count :: Num b => Event a -> m (Dynamic b)
116+-- Initial Bool value is supplied; toggles at each Event firing.
117+[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
118+
119+-- Transform Dynamic to Dynamic using function
120+[H] forDyn :: Dynamic a -> (a -> b) -> m (Dynamic b)
121+[H] mapDyn :: (a -> b) -> Dynamic a -> m (Dynamic b)
122+[H] mapDynM :: (a -> m' b) -> Dynamic a -> m (Dynamic b)
123+ -- Note m' supplies [S] context
124+[H] splitDyn :: Dynamic (a, b) -> m (Dynamic a, Dynamic b)
125+
126+-- Combine multiple Dynamics
127+[H] mconcatDyn :: Monoid a => [Dynamic a] -> m (Dynamic a)
128+[H] distributeDMapOverDyn :: GCompare k => DMap (WrapArg Dynamic k) -> m (Dynamic (DMap k))
129+[H] combineDyn :: (a -> b -> c) -> Dynamic a -> Dynamic b -> m (Dynamic c)
130+
131+-- Efficient one-to-many fanout
132+[ ] demux :: Ord k => Dynamic k -> Demux k
133+[H] getDemuxed :: Eq k => Demux k -> k -> m (Dynamic Bool)
134+
135+-- Dynamic to Dynamic, removing updates w/o value change
136+[ ] nubDyn :: Eq a => Dynamic a -> Dynamic a
137+
138+-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
139+[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
140+[ ] traceDynWith :: (a -> String) -> Dynamic a -> Dynamic a
141+```
142+
143+## Flattening functions
144+
145+These functions flatten nested types such as Event-of-Event, Behavior-of-Event, Event-of-Behavior etc, removing the outer wrapper.
146+
147+For Events, the returned Event fires whenever the latest Event supplied by the wrapper fires. There are differences in how the functions handle *coincidences* -- situations where the old or new Event fires at the same instant that we switch from old to new. In some cases, the output Event tracks the old Event at the instant of switchover, while in other cases it tracks the new Event.
148+
149+```haskell
150+-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
151+[ ] switch :: Behavior (Event a) -> Event a
152+
153+-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
154+[ ] switchPromptlyDyn :: Dynamic (Event a) -> Event a
155+
156+-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
157+[ ] coincidence :: Event (Event a) -> Event a
158+
159+-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
160+-- Output updated whenever inner OR outer Dynamic updates.
161+[ ] joinDyn :: Dynamic (Dynamic a) -> Dynamic a
162+[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
163+
164+-- Analogous to 'hold': Create a Behavior that is initially identical to the
165+-- supplied Behavior. Updated to track a new Behavior whenever the Event fires.
166+[H] switcher :: Behavior a -> Event (Behavior a) -> m (Behavior a)
167+
168+-- Similar to above, for Events. Created Event initially tracks the first argument.
169+-- At switchover, the output Event immediately tracks the new Event.
170+[H] switchPromptly :: Event a -> Event (Event a) -> m (Event a)
171+```
README.md+2
@@ -12,4 +12,6 @@ Comprehensive documentation is still a work in progress, but a tutorial for Refl
12
12
13[reddit/r/reflexfrp](http://www.reddit.com/r/reflexfrp)
13[reddit/r/reflexfrp](http://www.reddit.com/r/reflexfrp)
14
14
15+[reflex at hackage](https://hackage.haskell.org/package/reflex)
16+
15irc.freenode.net #reflex-frp
17irc.freenode.net #reflex-frp
default.nix+3−1
@@ -1,12 +1,14 @@
1{ mkDerivation, dependent-map, dependent-sum
1{ mkDerivation, dependent-map, dependent-sum
2, mtl, ref-tf, semigroups, these, MemoTrie, exception-transformers
2, mtl, ref-tf, semigroups, these, MemoTrie, exception-transformers
3+, haskell-src-exts, haskell-src-meta
3}:
4}:
4mkDerivation {
5mkDerivation {
5 pname = "reflex";
6 pname = "reflex";
6− version = "0.3";
7+ version = "0.3.1";
7 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
8 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
8 buildDepends = [
9 buildDepends = [
9 dependent-map dependent-sum mtl ref-tf semigroups these exception-transformers
10 dependent-map dependent-sum mtl ref-tf semigroups these exception-transformers
11+ haskell-src-exts haskell-src-meta
10 ];
12 ];
11 testDepends = [
13 testDepends = [
12 MemoTrie
14 MemoTrie
reflex.cabal+5−2
@@ -1,5 +1,5 @@
1Name: reflex
1Name: reflex
2−Version: 0.3
2+Version: 0.3.1
3Synopsis: Higher-order Functional Reactive Programming
3Synopsis: Higher-order Functional Reactive Programming
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5License: BSD3
5License: BSD3
@@ -28,7 +28,10 @@ library
28 ref-tf == 0.4.*,
28 ref-tf == 0.4.*,
29 exception-transformers == 0.4.*,
29 exception-transformers == 0.4.*,
30 transformers >= 0.2,
30 transformers >= 0.2,
31− transformers-compat >= 0.3
31+ transformers-compat >= 0.3,
32+ haskell-src-exts == 1.16.*,
33+ haskell-src-meta == 0.6.*,
34+ syb == 0.5.*
32
35
33 exposed-modules:
36 exposed-modules:
34 Reflex,
37 Reflex,
src/Reflex/Dynamic.hs+2
@@ -17,6 +17,8 @@ module Reflex.Dynamic ( Dynamic -- Abstract so we can preserve the law that the
17 , mapDynM
17 , mapDynM
18 , foldDyn
18 , foldDyn
19 , foldDynM
19 , foldDynM
20+ , foldDynMaybe
21+ , foldDynMaybeM
20 , combineDyn
22 , combineDyn
21 , collectDyn
23 , collectDyn
22 , mconcatDyn
24 , mconcatDyn
src/Reflex/Dynamic/TH.hs+30−1
@@ -1,11 +1,17 @@
1{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TypeOperators, GADTs, EmptyDataDecls, PatternGuards #-}
1{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TypeOperators, GADTs, EmptyDataDecls, PatternGuards #-}
2−module Reflex.Dynamic.TH (qDyn, unqDyn) where
2+module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
3
3
4import Reflex.Dynamic
4import Reflex.Dynamic
5
5
6import Language.Haskell.TH
6import Language.Haskell.TH
7+import qualified Language.Haskell.TH.Syntax as TH
8+import Language.Haskell.TH.Quote
7import Data.Data
9import Data.Data
8import Control.Monad.State
10import Control.Monad.State
11+import qualified Language.Haskell.Exts as Hs
12+import qualified Language.Haskell.Meta.Syntax.Translate as Hs
13+import Data.Monoid
14+import Data.Generics
9
15
10-- | 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'
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'
11qDyn :: Q Exp -> Q Exp
17qDyn :: Q Exp -> Q Exp
@@ -36,3 +42,26 @@ data UnqDyn
36--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
42--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
37unqMarker :: a -> UnqDyn
43unqMarker :: a -> UnqDyn
38unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
44unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
45+
46+mkDyn :: QuasiQuoter
47+mkDyn = QuasiQuoter
48+ { quoteExp = mkDynExp
49+ , quotePat = error "mkDyn: pattern splices are not supported"
50+ , quoteType = error "mkDyn: type splices are not supported"
51+ , quoteDec = error "mkDyn: declaration splices are not supported"
52+ }
53+
54+mkDynExp :: String -> Q Exp
55+mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
56+ Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
57+ Hs.ParseOk e -> qDyn $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
58+ where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
59+ antiE x = case x of
60+ Hs.SpliceExp se ->
61+ Hs.App (Hs.Var $ Hs.Qual (Hs.ModuleName modName) (Hs.Ident occName)) $ case se of
62+ Hs.IdSplice v -> Hs.Var $ Hs.UnQual $ Hs.Ident v
63+ Hs.ParenSplice ps -> ps
64+ _ -> x
65+ reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
66+ | modName == modName' && occName == occName' = 'unqMarker
67+ reinstateUnqDyn x = x
src/Reflex/Host/Class.hs+37−1
@@ -4,6 +4,7 @@ module Reflex.Host.Class where
4import Reflex.Class
4import Reflex.Class
5
5
6import Control.Applicative
6import Control.Applicative
7+import Control.Monad
7import Control.Monad.Fix
8import Control.Monad.Fix
8import Control.Monad.Trans
9import Control.Monad.Trans
9import Control.Monad.Trans.Reader (ReaderT())
10import Control.Monad.Trans.Reader (ReaderT())
@@ -12,7 +13,8 @@ import Control.Monad.Trans.Cont (ContT())
12import Control.Monad.Trans.Except (ExceptT())
13import Control.Monad.Trans.Except (ExceptT())
13import Control.Monad.Trans.RWS (RWST())
14import Control.Monad.Trans.RWS (RWST())
14import Control.Monad.Trans.State (StateT())
15import Control.Monad.Trans.State (StateT())
16+import qualified Control.Monad.Trans.State.Strict as Strict
15−import Data.Dependent.Sum (DSum)
17+import Data.Dependent.Sum (DSum (..))
16import Data.Monoid
18import Data.Monoid
17import Data.GADT.Compare
19import Data.GADT.Compare
18import Control.Monad.Ref
20import Control.Monad.Ref
@@ -113,6 +115,25 @@ newEventWithTriggerRef = do
113 return (e, rt)
115 return (e, rt)
114{-# INLINE newEventWithTriggerRef #-}
116{-# INLINE newEventWithTriggerRef #-}
115
117
118+fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
119+fireEventRef mtRef input = do
120+ mt <- readRef mtRef
121+ case mt of
122+ Nothing -> return ()
123+ Just trigger -> fireEvents [trigger :=> input]
124+
125+fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
126+fireEventRefAndRead mtRef input e = do
127+ mt <- readRef mtRef
128+ case mt of
129+ Nothing -> return Nothing -- Since we aren't firing the input, the output can't fire
130+ Just trigger -> fireEventsAndRead [trigger :=> input] $ do
131+ mGetValue <- readEvent e
132+ case mGetValue of
133+ Nothing -> return Nothing
134+ Just getValue -> liftM Just getValue
135+
136+
116--------------------------------------------------------------------------------
137--------------------------------------------------------------------------------
117-- Instances
138-- Instances
118--------------------------------------------------------------------------------
139--------------------------------------------------------------------------------
@@ -152,6 +173,21 @@ instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
152 type ReadPhase (StateT s m) = ReadPhase m
173 type ReadPhase (StateT s m) = ReadPhase m
153 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
174 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
154 runHostFrame = lift . runHostFrame
175 runHostFrame = lift . runHostFrame
176+
177+
178+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
179+ newEventWithTrigger = lift . newEventWithTrigger
180+ newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
181+
182+instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) where
183+ subscribeEvent = lift . subscribeEvent
184+
185+instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
186+ type ReadPhase (Strict.StateT s m) = ReadPhase m
187+ fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
188+ runHostFrame = lift . runHostFrame
189+
190+
155
191
156instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
192instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
157 newEventWithTrigger = lift . newEventWithTrigger
193 newEventWithTrigger = lift . newEventWithTrigger
test/Reflex/Pure.hs+37−7
@@ -1,6 +1,10 @@
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
1−{- | This module provides a pure implementation of Reflex, which is intended to serve as a reference for the semantics of the Reflex class. All implementations of Reflex should produce the same results as this implementation, although performance and laziness/strictness may differ.
3+of Reflex should produce the same results as this implementation, although performance
4+and laziness/strictness may differ.
2-}
5-}
6+
3−{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, EmptyDataDecls #-}
7+{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, EmptyDataDecls, InstanceSigs #-}
4module Reflex.Pure where
8module Reflex.Pure where
5
9
6import Reflex.Class
10import Reflex.Class
@@ -8,39 +12,65 @@ import Data.Functor.Misc
8
12
9import Control.Monad
13import Control.Monad
10import Data.MemoTrie
14import Data.MemoTrie
15+import Data.Dependent.Map (DMap, GCompare)
11import qualified Data.Dependent.Map as DMap
16import qualified Data.Dependent.Map as DMap
12
17
13data Pure t
18data Pure t
14
19
15−-- | The Enum instance of t must be dense: for all x :: t, there must not exist any y :: t such that pred x < y < x
20+-- | The Enum instance of t must be dense: for all x :: t, there must not exist
21+-- any y :: t such that pred x < y < x. The HasTrie instance will be used exclusively
16−-- The HasTrie instance will be used exclusively to memoize functions of t, not for any of its other capabilities
22+-- to memoize functions of t, not for any of its other capabilities.
17instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
23instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
24+
18 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
25 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
19− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
26+ newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
27+
20 type PushM (Pure t) = (->) t
28 type PushM (Pure t) = (->) t
21 type PullM (Pure t) = (->) t
29 type PullM (Pure t) = (->) t
30+
31+ never :: Event (Pure t) a
22 never = Event $ \_ -> Nothing
32 never = Event $ \_ -> Nothing
33+
34+ constant :: a -> Behavior (Pure t) a
23 constant x = Behavior $ \_ -> x
35 constant x = Behavior $ \_ -> x
36+
37+ push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
24 push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
38 push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
39+
40+ pull :: PullM (Pure t) a -> Behavior (Pure t) a
25 pull = Behavior . memo
41 pull = Behavior . memo
42+
43+ merge :: GCompare k => DMap (WrapArg (Event (Pure t)) k) -> Event (Pure t) (DMap k)
26 merge events = Event $ memo $ \t ->
44 merge events = Event $ memo $ \t ->
27 let currentOccurrences = unwrapDMapMaybe (($ t) . unEvent) events
45 let currentOccurrences = unwrapDMapMaybe (($ t) . unEvent) events
28 in if DMap.null currentOccurrences
46 in if DMap.null currentOccurrences
29 then Nothing
47 then Nothing
30 else Just currentOccurrences
48 else Just currentOccurrences
49+
50+ fan :: GCompare k => Event (Pure t) (DMap k) -> EventSelector (Pure t) k
31 fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
51 fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
52+
53+ switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
32 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
54 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
55+
56+ coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
33 coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
57 coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
34
58
35instance Ord t => MonadSample (Pure t) ((->) t) where
59instance Ord t => MonadSample (Pure t) ((->) t) where
60+
61+ sample :: Behavior (Pure t) a -> (t -> a)
36 sample = unBehavior
62 sample = unBehavior
37
63
38instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
64instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
65+
66+ hold :: a -> Event (Pure t) a -> (t -> (Behavior (Pure t) a))
39 hold initialValue e initialTime = Behavior f
67 hold initialValue e initialTime = Behavior f
40 where f = memo $ \sampleTime ->
68 where f = memo $ \sampleTime ->
69+ -- Really, the sampleTime should never be prior to the initialTime,
41− if sampleTime <= initialTime -- Really, the sampleTime should never be prior to the initialTime, because that would mean the Behavior is being sampled before being created
70+ -- because that would mean the Behavior is being sampled before being created.
71+ if sampleTime <= initialTime
42 then initialValue
72 then initialValue
43 else let lastTime = pred sampleTime
73 else let lastTime = pred sampleTime
44 in case unEvent e lastTime of
74 in case unEvent e lastTime of
45 Nothing -> f lastTime
75 Nothing -> f lastTime
46− Just x -> x
76+ Just x -> x