Is it possible to manually update the value of a Behaviour? (Functional Reactive Programming, Threepenny)

Solution 1:

Full credit to this response from duplode, I'll just go through how it was solved:

Let's say we have a function that modifies a list somehow, depending on some value. How/why updateMyList modifies the list doesn't really matter for this explanation, we just need to know its type. For this example, we'll say the value that determines how the list changes is a mouse coordinate tuple (x, y), which we'll pass as its first parameter:

updateMyList :: (Double, Double) -> [Integer] -> [Integer]
updateMyList (x, y) oldList = ...

If we have an Event that tells us the mouse coordinates when the user clicks:

mouseCoords :: Behavior (Double, Double)
mouseCoords <- stepper (0,0) $ UI.mousemove canvas

mouseClicked :: Event (Double, Double)
mouseClicked = mouseCoords <@ UI.click canvas -- this is the Event we need

What we need to do is fmap the list-updating function onto mouseClicked:

listChangeEvent = fmap updateMyList mouseClicked

So we've created a new Event: when mouseClicked is triggered, the mouse coordinates are passed as the first parameter to updateMyList, and that is the value of our new Event at that timestamp. But this is a partially applied function, updateMyList still requires an [Integer] as a parameter, so as a result, listChangeEvent has the following type:

listChangeEvent :: Event ([Integer] -> [Integer])

Now, this is the clever part: if we use accumB and specify the starting accumulator (i.e. our starting list, [1,2,3,4]), and then also use the above listChangeEvent as the Event accumB takes its value from:

listState      <- accumB ([1,2,3,4]) listChangeEvent

Then that accumulator is what will be passed to the function in Event ([Integer] -> [Integer]). Meaning the first time the listChangeEvent triggers, updateMyList will be called with:

updateMyList (x, y) [1,2,3,4] -- (x, y) being the mouse coordinates at that time

And the result of that becomes the new accumulator value in listState, and that new list will be used as the parameter to updateMyList the next time listChangeEventtriggers, and so on.

We can use this for anything at all, it doesn't necessarily have to be a list that we're modifying. This just gives us a way to initialize a Behavior with a value, and that we can specify exactly how the next value of the Behavior is derived, by creating a function that is equivalent to updateMyList.