
liftA
3
:: Applicative f ⇒ (a → b → c → d)
→ f a → f b → f c → f d
liftA
3
f a b c = liftA
2
f a b <∗> c
...
The left-associative (<$>) is just a synonym for fmap—a stylistic
preference—while liftA
2
, liftA
3
, etc. are generalizations of the
monadic combinators liftM
2
, liftM
3
, etc.
CFRP’s lift
0
corresponds to pure, while lift
2
, lift
3
, etc corre-
spond to liftA
2
, liftA
3
, etc., so the Applicative instance replaces
all of the lift
n
.
4
Functions, and hence B, form an applicative functor, where
pure and (<∗>) correspond to the classic K and S combinators:
instance Applicative ((→) t) where
pure = const
f <∗> g = λt → (f t) (g t )
The Applicative instance for functions leads to the semantics
of the Behavior instance of Applicative. As with Functor above,
the semantic function distributes over the class methods, i.e., at is
an applicative functor morphism:
instance
sem
Applicative Behavior where
at (pure a) = pure a
= const a
at (b
f
<∗> b
x
) = at b
f
<∗> at b
x
= λt → (b
f
‘at‘ t) (b
x
‘at‘ t)
So, given a function-valued behavior b
f
and an argument-valued
behavior b
x
, to sample b
f
<∗> b
x
at time t, sample b
f
and b
x
at t
and apply one result to the other.
This (<∗>) operator is the heart of FRP’s concurrency model,
which is semantically determinate, synchronous, and continuous.
2.1.3 Monad
Although Behavior is a semantic Monad as well, the implemen-
tation developed in Section 5 does not implement Monad .
2.2 Events
Like behaviors, much of the event functionality can be packaged
via standard type classes.
2.2.1 Monoid
Classic FRP had a never-occurring event and an operator to merge
two events. Together, these combinators form a monoid, so ∅ and
(⊕) (Haskell’s mempty and mappend) replace the CFRP names
neverE and (.|.).
The event monoid differs from the list monoid in that (⊕) must
preserve temporal monotonicity.
instance
sem
Monoid (Event a) where
occs ∅ = [ ]
occs (e ⊕ e
0
) = occs e ‘merge‘ occs e
0
Temporal merging ensures a time-ordered result and has a left-bias
in the case of simultaneity:
merge :: E
a
→ E
a
→ E
a
[ ] ‘merge‘ vs = vs
us ‘merge‘ [ ] = us
((
ˆ
t
a
, a) : ps) ‘merge‘ ((
ˆ
t
b
, b) : qs)
|
ˆ
t
a
6
ˆ
t
b
= (
ˆ
t
a
, a) : (ps ‘merge‘ ((
ˆ
t
b
, b) : qs))
| otherwise = (
ˆ
t
b
, b) : (((
ˆ
t
a
, a) : ps) ‘merge‘ qs)
Note that occurrence lists may be infinitely long.
4
The formulation of the lift
n
in terms of operators corresponding to pure
and (<∗>) was noted in (Elliott 1998a, Section 2.1).
2.2.2 Functor
Mapping a function over an event affects just the occurrence values,
leaving the times unchanged.
instance
sem
Functor Event where
occs (fmap f e) = map (λ(
ˆ
t
a
, a) → (
ˆ
t
a
, f a)) (occs e)
2.2.3 Monad
Previous FRP definitions and implementations did not have a
monad instance for events. Such an instance, however, is very
useful for dynamically-generated events. For example, consider
playing Asteroids and tracking collisions. Each collision can break
an asteroid into more of them (or none), each of which has to be
tracked for more collisions. Another example is a chat room hav-
ing an enter event whose occurrences contain new events like speak
(for the newly entered user).
A unit event has one occurrence, which is always available:
occs (return a) = [(−∞, a)]
The join operation collapses an event-valued event ee:
join
E
:: Event (Event a) → Event a
Each occurrence of ee delivers a new event, all of which get merged
together into a single event.
occs (join
E
ee) =
foldr merge [ ] ◦ map delayOccs ◦ occs ee
delayOccs :: (
b
T , Event a) → E
a
delayOccs (
ˆ
t
e
, e) = [(
ˆ
t
e
‘max ‘
ˆ
t
a
, a) | (
ˆ
t
a
, a) ← occs e ]
Here, delayOccs ensures that inner events cannot occur before they
are generated.
This definition of occs hides a subtle problem. If ee has in-
finitely many non-empty occurrences, then the foldr , if taken as
an implementation, would have to compare the first occurrences of
infinitely many events to see which is the earliest. However, none
of the occurrences in delayOccs (
ˆ
t
e
, e) can occur before time
ˆ
t
e
, and the delayOccs applications are given monotonically non-
decreasing times. So, only a finite prefix of the events generated
from ee need be compared at a time.
2.2.4 Applicative functor
Any monad can be made into an applicative functor, by defining
pure = return and (<∗>) = ap. However, this Applicative
instance is unlikely to be very useful for Event. Consider function-
and argument-valued events e
f
and e
x
. The event e
f
<∗> e
x
would
be equivalent to e
f
‘ap‘ e
x
and hence to
e
f
>>= λf → e
x
>>= λx → return (f x )
or more simply
e
f
>>= λf → fmap f e
x
The resulting event contains occurrences for every pair of occur-
rences of e
f
and e
x
, i.e., (
ˆ
t
f
‘max ‘
ˆ
t
x
, f x ) for each (
ˆ
t
f
, f ) ∈
occs e
f
and (
ˆ
t
x
, x ) ∈ occs e
x
. If there are m occurrences of e
f
and n occurrences of e
x
, then there will m × n occurrences of
e
f
<∗> e
x
. Since the maximum of two values is one value or the
other, there are at most m +n distinct values of
ˆ
t
f
‘max ‘
ˆ
t
x
. Hence
the m × n occurrences must all occur in at most m + n tempo-
rally distinct clusters. Alternatively, one could give a relative time
semantics by using (+) in place of max .
2.3 Combining behaviors and events
FRP’s basic tool for introducing reactivity combines a behavior and
and an event.