FRP与状态机配镜头循环镜头

时间:2018-01-05 07:52:40

标签: functional-programming state reactive-programming frp

我试图理解FRP图和带镜头的状态机之间的实用差异 - 特别是对于像游戏循环这样的事情,其中​​每个滴答都重新绘制整个状态。

使用javascript语法,以下实现基本上都可以工作:

选项1:带镜头的状态机

//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);
});

选项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)

我看到Option 1允许任何更新在游戏状态的任何位置获取或设置数据,但Option 2中的所有单元格/行为都可以调整为类型GameState并且然后同样的事情适用。如果是这种情况,那么我对这种差异感到非常困惑,因为那样只会归结为:

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

然后他们真的非常相同......

实现该目标的另一种方法是将所有Cell存储在某个全局引用中,然后任何其他单元可以对它们进行采样以供读取。可以传播新的更新以进行通信。该解决方案在一天结束时也与选项1非常相似。

在这种情况下,有没有办法构建FRP图,使其比事件驱动的状态机具有明显的优势?

1 个答案:

答案 0 :(得分:2)

我不太确定你的问题是什么,也因为你不断改变解释性文字中的第二个例子。

在任何情况下,FRP方法的主要好处 - 正如我所看到的 - 如下:游戏状态取决于许多事情,但它们都明确列在定义的右侧cGameState

相反,在命令式样式中,您有一个全局变量state,可能会也可能不会被您刚才提供的代码中未显示的代码更改。据我所知,下一行可能是

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

并且游戏状态突然取决于第二个事件来源,这一事实从您展示的片段中看不出来。这在FRP示例中不会发生:cGameState的所有依赖关系在右侧都是明确的。 (当然,它们可能非常复杂,但至少它们是明确的。)