I have looked at a number answers to questions here on Stack Overflow trying to find a solution to my problem in using the Reactive Banana library. All the answers use some magic using 'mapAccum' that I can't quite understand. Looking at the API documentation all I find is "Efficient combination of accumE
and accumB
." which is not very helpful.
It seems that this function can be used to compare the values of a Behavior
at the time of two consecutive events which is what I'd like to do. But I not clear how to make that work.
How exactly does mapAccum
work?
The
mapAccum
function is very similar to themapAccumL
function from the standard Data.List module, hence the name. The documentation on Hackage also includes a link to the source code ofmapAccum
. Together with the type signature, this is hopefully enough information to figure out how this function works.Then again, I could just improve the documentation. :-) But I'm not entirely clear on how to do that short of pasting the source code. The second part of the result is easy to describe by the following equation
But the first part does not have such a nice equation.
I could write a description in words:
but I'm not sure whether these words actually help.
Notice that
so it takes an initial value
:: acc
to accumulate on, and an Event which produces a function that updates that accumulated value whilst producing an output value::x
. (Typically you'd make such an event by partially applying some function via<$>
.) As a result you get a new Event that fires yourx
values whenever they turn up and a Behaviour containing your current accumulated value.Use
mapAccum
if you have an event and you want to make a related behaviour and event.For example in your problem domain from your other question, suppose you have an event
eTime :: Event t Int
that fired erratically and you wanted to calculateeDeltaTime :: Event t Int
for the differences andbTimeAgain :: Behaviour t Int
for the currently used time:I could have written that
getDelta new = \old -> (new-old,new)
to make the next step clearer:In this case,
bTimeAgain
would be a behaviour with the same value as the events ineTime
. This happens because mygetDelta
function passesnew
straight through unchanged fromeTime
to theacc
value. (If I wantedbTimeAgain
on its own, I would have usedstepper :: a -> Event t a -> Behaviour t a
.) If I don't needbTimeAgain
, I could just write(eDeltaT,_) = mapAccum 0 $ deltaMaker
.Note: I'm using an answer so I can write formatted code
Here is my attempt at documenting the function:
This function is the analog to the
mapAccumL
function from theData.List
module. It is an efficient combination ofaccumE
andaccumB
. The resultingBehavior
is useful for, among other things, for holding the history of previous events which might be needed to compute the values (x) of an event stream.Example: compute the roling average of the last 5 events