与玻璃钢战斗
Fighting with FRP
我读过有关 FRP 的文章,感到非常兴奋。
它看起来很棒,所以你可以写更多的高级代码,一切都更可组合,等等
然后我尝试用几百个 sloc 从普通 js 到 Bacon 重写我自己的小游戏。
而且我发现,与其编写仅包含高级逻辑的代码,我实际上还在击败 Bacon.js 及其对原则的遵守。
我 运行 有点头疼,主要是干扰干净的代码
.take(1)
与其获取价值,不如创建丑陋的结构。
- 循环依赖
有时他们应该符合逻辑。但是在 FRP 中实现它是可怕的
- 活动状态
甚至 bacon.js 的创建者也有 troubles。
这里的示例是演示问题的代码和平:
任务是不允许两个玩家呆在同一个地方
通过 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);
我要演示的是:
me.positions
对自己有依赖性
- 理解这段代码并不容易。 Here 是命令式的实现。而且看起来更容易理解。我花了更多时间在培根实施上。结果我不确定它是否会按预期工作。
我的问题:
可能我错过了一些基本的东西。也许我的实现不是 FRP 风格?
也许这段代码看起来还不错,只是不习惯新的编码风格?
还是这个众所周知的问题,我应该选择best of all evil?所以像描述的那样 FRP 的问题,或者 OOP 的问题。
我在尝试使用 Bacon 和 RxJs 编写游戏时也有过类似的经历。具有独立性的事物(例如玩家的位置)很难以 "pure FRP" 的方式处理。
例如,在我早期的 Worzone 游戏中,我包含了一个可变 targets 对象,可以查询玩家和怪物的位置。
另一种方法是像 Elm 的家伙那样做:将完整的游戏状态建模为单个 属性(或 Elm 中称为 Signal)并根据该完整状态计算下一个状态。
到目前为止我的结论是 FRP 不太适合游戏编程,至少在 "pure" 方面是这样。毕竟,对于某些事情,可变状态可能是更可组合的方法。在一些游戏项目中,例如 Hello World Open 赛车,我使用了可变状态,例如用于存储状态的 DOM 和用于传递事件的 EventStreams。
所以,Bacon.js 不是灵丹妙药。我建议你自己弄清楚,哪里可以应用FRP,哪里不能应用!
我有时也有类似的填充物。对我来说,用 FRP 编程的经历主要是解谜。其中有些很容易,有些则不然。而那些我觉得容易的可能对我的同事来说更难,反之亦然。我不喜欢 FRP。
不要误会我的意思,我喜欢解谜,这很有趣!但我认为在有偿工作中编程应该更……无聊。更可预测。并且代码应该尽可能简单,甚至是原始的。
当然,全局可变状态也不是我们应该走的路。我想我们应该想办法让 FRP 更无聊:)
还有关于您的代码的评论,我认为这将更像 FRP(未经测试的草稿):
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;
}
});
我读过有关 FRP 的文章,感到非常兴奋。 它看起来很棒,所以你可以写更多的高级代码,一切都更可组合,等等
然后我尝试用几百个 sloc 从普通 js 到 Bacon 重写我自己的小游戏。
而且我发现,与其编写仅包含高级逻辑的代码,我实际上还在击败 Bacon.js 及其对原则的遵守。
我 运行 有点头疼,主要是干扰干净的代码
.take(1)
与其获取价值,不如创建丑陋的结构。
- 循环依赖
有时他们应该符合逻辑。但是在 FRP 中实现它是可怕的
- 活动状态
甚至 bacon.js 的创建者也有 troubles。
这里的示例是演示问题的代码和平:
任务是不允许两个玩家呆在同一个地方
通过 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);
我要演示的是:
me.positions
对自己有依赖性- 理解这段代码并不容易。 Here 是命令式的实现。而且看起来更容易理解。我花了更多时间在培根实施上。结果我不确定它是否会按预期工作。
我的问题:
可能我错过了一些基本的东西。也许我的实现不是 FRP 风格?
也许这段代码看起来还不错,只是不习惯新的编码风格?
还是这个众所周知的问题,我应该选择best of all evil?所以像描述的那样 FRP 的问题,或者 OOP 的问题。
我在尝试使用 Bacon 和 RxJs 编写游戏时也有过类似的经历。具有独立性的事物(例如玩家的位置)很难以 "pure FRP" 的方式处理。
例如,在我早期的 Worzone 游戏中,我包含了一个可变 targets 对象,可以查询玩家和怪物的位置。
另一种方法是像 Elm 的家伙那样做:将完整的游戏状态建模为单个 属性(或 Elm 中称为 Signal)并根据该完整状态计算下一个状态。
到目前为止我的结论是 FRP 不太适合游戏编程,至少在 "pure" 方面是这样。毕竟,对于某些事情,可变状态可能是更可组合的方法。在一些游戏项目中,例如 Hello World Open 赛车,我使用了可变状态,例如用于存储状态的 DOM 和用于传递事件的 EventStreams。
所以,Bacon.js 不是灵丹妙药。我建议你自己弄清楚,哪里可以应用FRP,哪里不能应用!
我有时也有类似的填充物。对我来说,用 FRP 编程的经历主要是解谜。其中有些很容易,有些则不然。而那些我觉得容易的可能对我的同事来说更难,反之亦然。我不喜欢 FRP。
不要误会我的意思,我喜欢解谜,这很有趣!但我认为在有偿工作中编程应该更……无聊。更可预测。并且代码应该尽可能简单,甚至是原始的。
当然,全局可变状态也不是我们应该走的路。我想我们应该想办法让 FRP 更无聊:)
还有关于您的代码的评论,我认为这将更像 FRP(未经测试的草稿):
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;
}
});