如何解决箭头的一阶约束?

时间:2013-06-29 23:28:43

标签: haskell arrows yampa

我的意思是一阶约束

首先,我将通过箭头的一阶约束来解释我的意思: 由于箭头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)) aaccumHold' :: SF (a,Event (a -> a)) -> a

注意: ArrowApply没有SF的实例。我的理解是,定义一个也没有意义。有关详细信息,请参阅"Programming with Arrows"

3 个答案:

答案 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正在积累的内容。