For fun and to learn I'm trying to implement an undo system in my app using functional reactive programming. I have a stream of state changes, which need to be saved onto the undo stack. When the user clicks undo, I take a value from the stack and update application state accordingly.
The problem is that this update itself also generates an event in the state change stream. So what I would like is to derive another stream from state changes, which ommits state change right after undo.
A simple diagram:
states ----S----S----S----
undos -------U-----------
save ----S---------S----
The first line is the stream of application state changes, the second line are undo events triggered by the user and the third line is the stream I'd like to implement and listen to instead of the first stream.
What is the best way to express such intent in FRP?
In RxJS:
var state = Rx.Observable.interval(2000)
.map(s => ({type:'s', value: s}))
.take(3);
var undo = Rx.Observable.interval(3000)
.map(u => ({type:'u', value: u}; }))
.take(1);
var save = state.merge(undo).scan((prev, curr) =>
if (prev.type === 'u') {
return {type: 'removed'};
} else {
return curr;
}
}).filter(x => x.type !== 'removed' && x.type !== 'u');
See this JSBin. The trick is merge is a vertical combinator (vertical with regards to the streams diagram), and scan is a horizontal combinator. Using both, first merge, then scan, you are able to build more powerful combinators like the one that solves your problem. The solution is similar in Bacon and Kefir.
With Bacon.js, I would use the Bacon.update
function to maintain a property with the whole undo stack. This way, we can support multiple successive undo steps.
var state = Bacon.sequentially(1000, [1,2,3])
var undo = Bacon.later(2500, "undo")
var stateStack = Bacon.update(
[],
state, function(prev, s) { return prev.concat(s) },
undo, function(prev, u) { return prev.slice(0, prev.length - 1) }
)
var currentState = stateStack.changes().map(function(stack) {
return stack[stack.length-1]
})
stateStack.log()
currentState.log()
来源:https://stackoverflow.com/questions/30006646/functional-reactive-operator-for-custom-filter-based-on-another-observable