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
scan
var 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
current
position. Anddelta
. I want to applydelta
tocurrent
, only ifcurrent after applying
will not be out of limits.current
depends ondelta
delta
depends oncurrent after applying
current after applying
depends 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.