forked from paf31/purescript-event
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Class.purs
133 lines (111 loc) · 4.37 KB
/
Class.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
module FRP.Event.Class
( class IsEvent
, fold
, folded
, count
, mapAccum
, withLast
, sampleOnRight
, (<|**>)
, sampleOnRightOp
, (<|*>)
, sampleOnRight_
, (<|*)
, sampleOnLeft
, (<**|>)
, sampleOnLeftOp
, (<*|>)
, sampleOnLeft_
, (*|>)
, applyOp
, (<**>)
, keepLatest
, fix
, gate
, gateBy
, module Data.Filterable
) where
import Prelude
import Control.Alternative (class Alternative, (<|>))
import Data.Compactable (compact)
import Data.Filterable (class Filterable, filterMap)
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Tuple (Tuple(..), snd)
-- | Functions which an `Event` type should implement:
-- |
-- | - `fold`: combines incoming values using the specified function,
-- | starting with the specific initial value.
-- | - `keepLatest` flattens a nested event, reporting values only from the
-- | most recent inner event.
-- | - `sampleOn`: samples an event at the times when a second event fires.
-- | - `fix`: compute a fixed point, by feeding output events back in as
-- | inputs.
-- | - `bang`: A one-shot event that happens NOW.
class (Alternative event, Filterable event) <= IsEvent event where
keepLatest :: forall a. event (event a) -> event a
sampleOnRight :: forall a b. event a -> event (a -> b) -> event b
sampleOnLeft :: forall a b. event a -> event (a -> b) -> event b
fix :: forall i. (event i -> event i) -> event i
infixl 4 sampleOnRight as <|**>
infixl 4 sampleOnLeft as <**|>
sampleOnRightOp :: forall event a b. IsEvent event => event (a -> b) -> event a -> event b
sampleOnRightOp ef ea = sampleOnRight ef ((#) <$> ea)
infixl 4 sampleOnRightOp as <|*>
sampleOnLeftOp :: forall event a b. IsEvent event => event (a -> b) -> event a -> event b
sampleOnLeftOp ef ea = sampleOnLeft ef ((#) <$> ea)
infixl 4 sampleOnLeftOp as <*|>
applyOp :: forall event a b. Applicative event => event a -> event (a -> b) -> event b
applyOp ea ef = apply ((#) <$> ea) ef
infixl 4 applyOp as <**>
-- | Count the number of events received.
count :: forall event a. IsEvent event => event a -> event Int
count = fold (\n _ -> n + 1) 0
-- | Combine subsequent events using a `Monoid`.
folded :: forall event a. IsEvent event => Monoid a => event a -> event a
folded = fold append mempty
-- | Compute differences between successive event values.
withLast :: forall event a. IsEvent event => event a -> event { now :: a, last :: Maybe a }
withLast e = filterMap identity (fold step Nothing e)
where
step Nothing a = Just { now: a, last: Nothing }
step (Just { now: b }) a = Just { now: a, last: Just b }
-- | Map over an event with an accumulator.
-- |
-- | For example, to keep the index of the current event:
-- |
-- | ```purescript
-- | mapAccum (\x i -> Tuple (i + 1) (Tuple x i)) 0`.
-- | ```
mapAccum :: forall event a b c. IsEvent event => (a -> b -> Tuple a c) -> a -> event b -> event c
mapAccum f acc xs = filterMap snd
$ fold (\(Tuple a _) b -> pure <$> f a b) (Tuple acc Nothing) xs
-- | Create an `Event` which samples the latest values from the first event
-- | at the times when the second event fires, ignoring the values produced by
-- | the second event.
sampleOnRight_ :: forall event a b. IsEvent event => event a -> event b -> event a
sampleOnRight_ a b = sampleOnRight a (const identity <$> b)
infixl 4 sampleOnRight_ as <|*
sampleOnLeft_ :: forall event a b. IsEvent event => event a -> event b -> event b
sampleOnLeft_ a b = sampleOnLeft a (const <$> b)
infixl 4 sampleOnLeft_ as *|>
-- | Sample the events that are fired while a boolean event is true. Note that,
-- | until the boolean event fires, it will be assumed to be `false`, and events
-- | will be blocked.
gate :: forall a event. IsEvent event => event Boolean -> event a -> event a
gate = gateBy (\x _ -> fromMaybe false x)
-- | Generalised form of `gateBy`, allowing for any predicate between the two
-- | events. The predicate will not be evaluated until a value from the first event is received.
gateBy
:: forall a b event
. IsEvent event
=> (Maybe a -> b -> Boolean)
-> event a
-> event b
-> event b
gateBy f sampled sampler = compact $
(\p x -> if f p x then Just x else Nothing)
<$> (pure Nothing <|> Just <$> sampled)
<|*> sampler
-- | Fold over values received from some `Event`, creating a new `Event`.
fold :: forall event a b. IsEvent event => (b -> a -> b) -> b -> event a -> event b
fold f b e = fix \i -> sampleOnRight (i <|> pure b) ((flip f) <$> e)