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 listChangeEvent
triggers, 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
.