davidkomer
davidkomer

Reputation: 3078

FRP vs. State Machine w/ Lenses for Game Loop

I'm trying to understand the practical difference between a FRP graph and a State Machine with lenses- specifically for something like a game loop where the entire state is re-drawn every tick.

Using javascript syntax, the following implementations would both essentially work:

Option 1: State Machine w/ Lenses

//Using Sanctuary and partial.lenses (or Ramda) primitives
//Each update takes the state, modifies it with a lens, and returns it

let state = initialValues;
eventSource.addEventListener(update, () => {
  state = S.pipe([
    updateCharacter,
    updateBackground,
  ])
  (state) //the first call has the initial settings

  render(state);
});

Option 2: FRP

//Using Sodium primitives
//It's possible this isn't the best way to structure it, feel free to advise

cCharacter = sUpdate.accum(initialCharacter, updateCharacter)
cBackground = sUpdate.accum(initialBackground, updateBackground)
cState = cCharacter.lift(cBackground, mergeGameObjects)
cState.listen(render)

I see that Option 1 allows any update to get or set data anywhere in the game state, however all the cells/behaviors in Option 2 could be adjusted to be of type GameState and then the same thing applies. If this were the case, then I'm really confused about the difference since that would then just boil down to:

cGameState = sUpdate
  .accum(initialGameState, S.pipe(...updates))
  .listen(render)

And then they're really very equivalent...

Another way to achieve that goal would be to store all the Cells in some global reference, and then any other cell could sample them for reading. New updates could be propagated for communicating. That solution also feels quite similar to Option 1 at the end of the day.

Is there a way to structure the FRP graph in such a way that it offers clear advantages over the event-driven state machine, in this scenario?

Upvotes: 3

Views: 708

Answers (1)

Heinrich Apfelmus
Heinrich Apfelmus

Reputation: 11064

I'm not quite sure what your question is, also because you keep changing the second example in your explanatory text.

In any case, the key benefit of the FRP approach — as I see it — is the following: The game state depends on many things, but they are all listed explicitly on the right-hand side of the definition of cGameState.

In contrast, in the imperative style, you have a global variable state which may or may not be changed by code that is not shown in the snippet you just presented. For all I know, the next line could be

eventSource2.addEventListener(update, () => { state = state + 1; })

and the game state suddenly depends on a second event source, a fact that is not apparent from the snippet you showed. This cannot happen in the FRP example: All dependencies of cGameState are explicit on the right-hand side. (They may be very complicated, sure, but at least they are explicit.)

Upvotes: 3

Related Questions