I am reading Conal Elliot's paper "Declarative Event-Oriented Programming" which has its examples written using a Fran library which is now obsolete.
As I am learning FRP I try to implement those examples using reactive-banana. I seem to have no problem with this (but have to think a lot :)). The only thing I do not understand is how to correctly translate Fran's ifB
.
It seems to have this type signature:
ifB :: Behavior Bool -> Behavior a -> Behavior a -> Behavior a
But reactive-banana only has switchB
for this sort of thing.
Currently I managed to do it by adding an additional "trigger" event which will be used to switch behaviors, like this:
ifB :: MonadMoment m => Event b -> BoolB -> Behavior a -> Behavior a -> m (Behavior a)
ifB trigger condB b1 b2 = switchB b2 (switcherB <@ trigger)
where switcherB = (\x -> if x then b1 else b2) <$> condB
I am not sure if this is a correct and good solution. Is it performant?
Below is a snippet from the paper which uses ifB
:
editCPoint :: User -> S.Point2 -> CPoint
editCPoint u p0 = (pos, closeEnough)
where
pos, lastRelease :: Point2B
-- vvv this is the ifB in question!
pos = ifB grabbing (mouse u) lastRelease
lastRelease = stepper p0 (release ‘snapshot_‘ pos)
closeEnough, grabbing :: BoolB
closeEnough = distance2 pos (mouse u) <* grabDistance
grabbing = stepper False (grab -=> True .|. release -=> False)
grab, release :: Event ()
grab = lbp u ‘whenE‘ closeEnough
release = lbr u ‘whenE‘ grabbing
grabDistance :: RealB
grabDistance = 2 * pointSize
I managed to get this snippet working with reactive-banana, by using my implementation of ifB
and feeding it a cursor move
event so it gets updated regularly. I tried to feed it only mouse press/release events as triggers for switching and it didn't work correctly...
Did I do it wrong? Any advice on how to do better?
Behavior
has an Applicative
instance, which is enough to implement ifB
without dynamic switching:
ifB :: Behavior Bool -> Behavior a -> Behavior a -> Behavior a
ifB bB bT bF = (\b t f -> if b then t else f) <$> bB <*> bT <*> bF
Or, using bool
from Data.Bool
:
ifB :: Behavior Bool -> Behavior a -> Behavior a -> Behavior a
ifB bB bT bF = bool <$> bF <*> bT <*> bB