I've read about FRP and was very excited. It looks great, so you can write more high-level code, and everything is more composable, and etc.
Then I've tried to rewrite my own little game with a few hundreds sloc from plain js to Bacon.
And I found that instead of writing high-level logic-only code, I actually beating with Bacon.js and its adherence to principles.
I run into some headache that mostly interfere clean code
.take(1)
Instead of getting value, I should create ugly constructions.
- Circular dependencies
Sometimes they should be by logic. But implementing it in FRP is scary
- Active state
Even creator of bacon.js have troubles with it.
As example here is the peace of code to demonstrate the problem:
Task is to not allow two players stay at same place
Implemented with bacon.js
http://jsbin.com/zopiyarugu/2/edit?js,console
function add(a) {return function(b){return a + b}}
function nEq(a) {return function(b){return a !== b}}
function eq(a) {return function(b){return a === b}}
function always(val) {return function(){return val}}
function id(a){return a}
var Player = function(players, movement, initPos){
var me = {};
me.position = movement
.flatMap(function(val){
return me.position
.take(1)
.map(add(val))
})
.flatMap(function(posFuture){
var otherPlayerPositions = players
.filter(nEq(me))
.map(function(player){return player.position.take(1)})
return Bacon
.combineAsArray(otherPlayerPositions)
.map(function(positions){
return !positions.some(eq(posFuture));
})
.filter(id)
.map(always(posFuture))
})
.log('player:' + initPos)
.toProperty(initPos);
return me;
}
var moveA = new Bacon.Bus();
var moveB = new Bacon.Bus();
var players = [];
players.push(new Player(players, moveA, 0));
players.push(new Player(players, moveB, 10));
moveA.push(4);
moveB.push(-4);
moveA.push(1);
moveB.push(-1);
moveB.push(-1);
moveB.push(-1);
moveA.push(1);
moveA.push(-1);
moveB.push(-1);
What I want to demonstrate is:
me.positions
have dependency on its own- It's not easy to understand this code. Here is imperative implementation. And it looks much easier to understand. I spent much more time with bacon implementation. And in result I'm not sure that it will works as expected.
My question:
Probably I miss something fundamental. Maybe my implementation is not so in FRP style?
Maybe this code looks ok, and it just unaccustomed with new coding style?
Or this well-known problems, and I should choose best of all evil? So troubles with FRP like described, or troubles with OOP.