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.
Sometimes they should be by logic. But implementing it in FRP is scary
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 ownProbably 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.
I've had similar experiences when trying to write games with Bacon and RxJs. Things that have a self-dependency (like player's position) are tough to handle in a "pure FRP" way.
For example, in my early Worzone game I included a mutable targets object that can be queried for positions of players and monsters.
Another approach is to do as the Elm guys do: model the full game state as a single Property (or Signal as it's called in Elm) and calculate the next state based on that full state.
So far my conclusion is that FRP is not so well-suited for game programming, at least in a "pure" way. After all, mutable state might be the more composable approach for some things. In some game projects, like the Hello World Open car race, I've used mutable state, like the DOM for storing state and EventStreams for passing events around.
So, Bacon.js is not a silver bullet. I suggest you find out yourself, where to apply FRP and where not to!