In a previous SO question (Is it possible?: Behavior t [Behavior t a] -> Behavior t [a]) we were analyzing the existence of a Behavior
join
(to
(Author here.)
First note, that the behaviorNow
function is the monadic join
.
In reactive-banana-0.7, Behavior t
is not a monad beause that would have serious consequences for efficiency.
The first and most important problem is that behaviors can also be stateful. In conjunction with join
, this would lead to time leaks. The main indication of problems is that the starting time t
of the inner Behavior t
is the same as the outer one. For instance, consider the program
e :: Event t Int
b :: Int -> Behavior t Int
b x = accumB 0 $ (x+) <$ e
bb :: Behavior t (Behavior t Int)
bb = stepper (pure 0) $ b <$> e
The behavior join bb
would need to keep track of the whole history of the event e
in order to perform the accumulation in the definition of b
. In other words, the event e
could never be garbage collected -- a time leak.
A second problem is that internally, the implementation of Behavior t
also includes an event that keeps track of when the behavior changes. However, a liberal use of the join
combinator, for instance as implied by do
notation, would lead to rather convoluted calculations to determine whether the behavior has changed or not. This is contrary to the reason for keeping track in the first place: efficiency by avoiding expensive calculations.
The Reactive.Banana.Switch module offers various combinators that are cousins of the join
function, but avoid the first problem with cleverly chosen types. In particular:
switchB
function is the most direct analogue of join
.AnyMoment Identity
type is similar to the Behavior
type, but without state and without keeping track of changes. Consequently, it has a monad instance.