Fighting FRP

I read about FRP and was very excited. It looks great, so you can write more high-level code, and everything will be more complicated, etc.

Then I tried to rewrite my little game with several hundred slots from simple js to Bacon.

And I found that instead of writing high-level logic code, I actually beat Bacon.js and its commitment to principles.

I am facing some headache that mostly interferes with clean code

  • .take(1)

Instead of getting meaning, I have to create ugly designs.

  1. Cyclic dependencies

Sometimes they must be logical. But implementing it in FRP is scary

  1. Active state

Even the creator of bacon.js has problems with it .


An example is the world of code to demonstrate the problem:

The challenge is to prevent two players from staying in one place.

Implemented using 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); 

I want to demonstrate the following:

  • me.positions are dependent on their own
  • It is not easy to understand this code. Here is a mandatory implementation. And it looks a lot easier to understand. I spent a lot more time selling bacon. And as a result, Iโ€™m not sure that it will work as expected.

My question is:

Perhaps I missed something fundamental. Maybe my implementation is wrong in FRP style?

Maybe this code looks fine, and it's just not used to the new coding style?

Or are these known problems, and should I choose the best of all evil? So there are problems with FRP, as described, or problems with OOP.

+6
source share
2 answers

I had similar impressions when trying to write games with Bacon and RxJs. Things that are self-dependent (such as a playerโ€™s position) are hard to handle in a โ€œpure FRPโ€ way.

For example, in my early Worzone game, I included a modified target object that can be requested for player and monster positions.

Another approach is to do as Elm guys: to simulate the full state of the game as the only property (or signal, as it is called in Elm), and calculate the next state based on this full state.

So far, my conclusion is that FRP is not so good for game programming, at least in a "clean" way. After all, a volatile state may be a more complex approach to certain things. In some game projects, such as the Hello World Open race, I used volatile state, like DOM to store state and EventStream to pass events around.

So, Bacon.js is not a silver bullet. I suggest you find out where to use FRP and where not!

+4
source

I have a similar fill sometimes. For me, programming experience with FRP is basically a puzzle solution. Some are lightweight and some are not. And those that seem easy to me can be more difficult than my colleagues and vice versa. And I don't like it in FRP.

Don't get me wrong, I like to solve puzzles, it's a lot of fun! But I think programming in paid work should be more ... boring. More predictable. And the code should be as simple as possible, even primitive.

But, of course, the global volatile state is also not the way we should go. I think we should find a way to make FRP more boring :)


Also a note about your code, I think it will be more FRP'ish (not a proven project):

 var otherPlayerPositions = players .filter(nEq(me)) .map(function(player){return player.position}); otherPlayerPositions = Bacon.combineAsArray(otherPlayerPositions); me.position = otherPlayerPositions .sampledBy(movement, function(otherPositions, move) { return {otherPositions: otherPositions, move: move}; }) .scan(initPos, function(myCurPosition, restArgs) { var myNextPosition = myCurPosition + restArgs.move; if (!restArgs.otherPositions.some(eq(myNextPosition))) { return myNextPosition; } else { return myCurPosition; } }); 
+1
source

Source: https://habr.com/ru/post/984954/


All Articles