All examples uses Ramda as _ (it's clear what methods do in examples contexts) and kefir as frp (almost same API as in bacon.js)
I have a stream, that describes change of position.
var xDelta = frp
.merge([
up.map(_.multiply(1)),
down.map(_.multiply(-1))
])
.sampledBy(frp.interval(10, 0))
.filter();
It emits +1 when I press UP key, and -1 on DOWN.
To get position I scan this delta
var x = xDelta
.scan(_.add)
.toProperty(0);
That's work as expected. But I want to limit value of x from 0 to 1000.
To solve this problem I found two solution:
Change function in
scanvar x = xDelta.scan(function (prev, next) { var newPosition = prev + next; if (newPosition < 0 && next < 0) { return prev; } if (newPosition > 1000 && next > 0) { return prev; } return newPosition; }, 0);
It looks Ok, but later, as new rules will be introduced, this method will grow. So I mean it doesn't look composable and FRPy.
I have
currentposition. Anddelta. I want to applydeltatocurrent, only ifcurrent after applyingwill not be out of limits.currentdepends ondeltadeltadepends oncurrent after applyingcurrent after applyingdepends oncurrent
So it looks like circular dependency. But I solved it using
flatMap.var xDelta = frp .merge([ up.map(_.multiply(1)), down.map(_.multiply(-1)) ]) .sampledBy(frp.interval(10, 0)) .filter(); var possibleNewPlace = xDelta .flatMap(function (delta) { return x .take(1) .map(_.add(delta)); }); var outOfLeftBoundFilter = possibleNewPlace .map(_.lte(0)) .combine(xDelta.map(_.lte(0)), _.or); var outOfRightBoundFilter = possibleNewPlace .map(_.gte(1000)) .combine(xDelta.map(_.gte(0)), _.or); var outOfBoundFilter = frp .combine([ outOfLeftBoundFilter, outOfRightBoundFilter ], _.and); var x = xDelta .filterBy(outOfBoundFilter) .scan(_.add) .toProperty(0);You can see full code example at iofjuupasli/capture-the-sheep-frp
And it's working demo gh-pages
It works, but using circular dependencies is probably anti-pattern.
Is there a better way to solve circular dependency in FRP?
The second more general question
With Controller it's possible to read some values from two Model and depending on it's values update both of them.
So dependencies looks like:
---> Model
Controller ---|
---> Model
With FRP there is no Controller. So Model value should be declaratively calculated from other Model. But what if Model1 calculating from another Model2 which is the same, so Model2 calculates from Model1?
Model ----->
<----- Model
For example two players with collision detection: both players have position and movement. And movement of first player depends on position of second, and vice versa.
I'm still newbie in all this stuff. It's not easy to start think in declarative FRP style after years of imperative coding. Probably I'm missing something.