使用netwire的周期性与电线连接

时间:2014-05-17 17:30:12

标签: haskell functional-programming frp netwire

我试图在Haskell中编写实时交互式图形的框架。我一直试图通过使用Netwire 5来处理事情,但我似乎并没有很好地处理事情和依赖事物。在彼此身上。 For example,以下代码应在切换到(val + 1)之前生成val两秒钟,然后无限期地继续。

someWire :: Num a => a -> Wire s e m a a
someWire x = (-->) ((pure x) &&& (periodic 2) >>> until) (someWire (x + 1))

然而,这会造成某种内存泄漏,我的程序会停止并只是继续分配内存,直到我的系统崩溃。或者,这个定义

someWire :: Num a => a -> Wire s e m a a
someWire x = (-->) ((pure x) &&& (at 2) >>> until) (someWire (x + 1))

表现出我期望的方式:从val开始计算,每两秒更改一次值。有人可以解释一下这种行为吗?

2 个答案:

答案 0 :(得分:1)

关键见解是periodic立即生成事件

因此,当我们从这条线路中产生一个值时,我们必须将其评估为以下内容:

someWire x
(-->) ((pure x) &&& (periodic 2) >>> until) (someWire (x + 1))
(-->) (pure (x, Event _) >>> until) (someWire (x + 1))
(-->) *inhibition* (someWire (x + 1))
someWire (x + 1)

由于这不是尾递归,因此不允许垃圾收集器清理为线路分配的先前实例,并且内存不足(而不是获得无限循环)。

答案 1 :(得分:0)

让我们看一下工作版本:

import Control.Wire hiding (until)
import qualified Control.Wire (until) as W

someWire :: Num a => a -> Wire s e m a a
someWire x = (-->) (pure x &&& at 2 >>> W.until) (someWire (x + 1))

-- To test in GHCi: testWire clockSession_ $ someWire 3 

WireArrow s,因此如果我们能够遵循箭头组合器正在做的事情,将更容易理解发生了什么。

(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c')

(&&&)构建一个箭头,将其输入(在我们的例子中,可以被认为是驱动事件的心跳)分成一对,向每个组件应用一个箭头。在这里,我们用一个总是产生x的箭头和一个给出一个将在两秒钟内发生的事件的箭头。

(>>>) :: Category cat => cat a b -> cat b c -> cat a c

(>>>)只是箭头的组合(ArrowCategory的子类),以第一至第二顺序编写。因此,我们将pure x &&& at 2的输出发送到W.until

W.until :: Monoid e => Wire s e m (a, Event b) a

使用W.until进行组合会将生成与事件(例如pure x &&& at 2)配对的值的箭头转换为箭头,该箭头会生成一个值,直到事件发生。一旦发生这种情况,我们将使用(-->)切换到另一条线路。

现在应该更容易理解为什么你不能使用periodic代替atat只在一个瞬间发生,而periodic一直在发生,因此W.until永远不会终止。 (如果你仔细研究了这些来源,你会发现W.until做的事情类似于事件发生的折叠,这与直观的解释相符。)