我的意思是一阶约束
首先,我将通过箭头的一阶约束来解释我的意思: 由于箭头desugar的方式,你不能使用箭头指示符中预期箭头命令的本地绑定名称。
这是一个例子来说明:
proc x -> f -< x + 1
去世arr (\x -> x + 1) >>> f
和类似proc x -> g x -< ()
的人会去arr (\x -> ()) >>> g x
,其中第二个x
是自由变量。 GHC user guide解释了这一点并说当你的箭头也是一个monad时,你可以创建ArrowApply
的实例并使用app
来解决这个问题。像proc x -> g x -<< ()
这样的内容会变成arr (\x -> (g x, ())) >>> app
。
我的问题
Yampa使用此类型定义accumHold
函数:a -> SF (Event (a -> a)) a
。
由于箭头的这种一阶限制,我正在努力编写以下函数:
accumHoldNoiseR :: (RandomGen g, Random a) => (a,a) -> g -> SF (Event (a -> a)) a
accumHoldNoiseR r g = proc f -> do
n <- noiseR r g -< ()
accumHold n -< f
上述定义不起作用,因为n
在去除后不在范围内。
或者,类似于此函数,其中SF
对的第一部分是传递给accumHold
的初始值
accumHold' :: SF (a,Event (a -> a)) -> a
accumHold' = ...
是否有一些我缺少的组合或技巧?或者在没有ArrowApply
实例的情况下编写这些定义是不可能的?
tl; dr:是否可以在yampa中定义accumHoldNoiseR :: (RandomGen g, Random a) => (a,a) -> g -> SF (Event (a -> a)) a
或accumHold' :: SF (a,Event (a -> a)) -> a
?
注意: ArrowApply
没有SF
的实例。我的理解是,定义一个也没有意义。有关详细信息,请参阅"Programming with Arrows"。
答案 0 :(得分:3)
这是理论上的答案。请Roman Cheplyaka's answer查看此问题,该问题更多地涉及您要实现的目标的实际细节。
n
超出范围的原因是,要使其在范围内使用,您将拥有与monad相同的bind
或>>=
。它是使用先前计算的结果作为下一个计算的功能输入,使得像monad一样强大。
因此,当您可以创建ArrowApply实例时,您可以将n
作为函数参数提供给后续箭头。
Chris Kuklewicz正确地在his comment中指出-<<
会将n
带入范围 - 它还使用app
,因此您需要一个ArrowApply实例。
除非您使用ArrowApply。这就是ArrowApply的用途。
答案 1 :(得分:2)
noiseR
是一个信号函数;它产生随机数的流,而不只是一个随机数(为此,你只需使用randomR
中的System.Random
。
另一方面,accumHold
的第一个参数只是一个初始值。
所以这不仅仅是一些限制 - 它实际上阻止了你提交类型错误。
如果我理解你正在尝试做什么,那么只需使用randomR
即可。否则,请说明您需要noiseR
。
答案 2 :(得分:0)
为了帮助其他人了解我是如何解决这个问题的,我会回答我自己的问题。
我试图实现游戏乒乓。我想让球每轮以随机速度开始。我想用accumHold
来定义球的速度。我有一些像这样的代码:
ballPos = proc e -> mdo -- note the recursive do
{- some clipping calculations using (x,y) -}
...
vx <- accumHold 100 -< e `tag` collisionResponse paddleCollision
vy <- accumHold 100 -< e `tag` collisionResponse ceilingFloorCollision
(x,y) <- integral -< (vx,vy)
returnA -< (x,y)
我想用随机值替换100(可能来自noiseR
)。
我是如何解决这个问题的,而是积累在方向上,collisionResponse
只是翻转符号(最终我想要使用相对于墙/桨的速度角度):
ballPos = proc (initV, e) -> mdo
{- some clipping calculations using (x,y) -}
...
(iVx,iVy) <- hold (0,0) -< initV
vx <- accumHold 1 -< e `tag` collisionResponse paddleCollision
vy <- accumHold 1 -< e `tag` collisionResponse ceilingFloorCollision
(x,y) <- integral -< (iVx*vx,iVy*vy)
returnA -< (x,y)
经验教训:
您通常可以将要累积的值/状态分为描述其变化的行为和描述其当前值的“幅度”,将行为作为输入。在我的例子中,我将初始速度的大小分开,将其作为信号函数的输入传递,并使用accumHold
来计算碰撞时球的影响(行为)。因此无论初始速度如何,击中墙壁都会“反射”球。这正是accumHold
正在积累的内容。