3

I'm trying to write a framework for real-time interactive graphics in Haskell. I've been trying to get a handle on things by using Netwire 5, but I don't seem to have a good handle on how things "depend" on one another. For example, the following code should produce val for two seconds before switching to (val + 1), and then continuing on indefinitely.

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

However, this creates some sort of memory leak where my program stalls and just keeps allocating memory until something on my system crashes. Alternatively, this definition

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

behaves the way that I expect it to: count from val onwards having the value change every two seconds. Can someone please explain this behavior?

4

2 回答 2

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)

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

于 2014-05-17T20:15:09.653 回答
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 

Wires 是Arrows,因此如果我们能够遵循箭头组合器的操作,将更容易理解发生了什么。

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

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

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

(>>>)只是箭头的组合(Arrow是 的子类Category),以先到后继的顺序编写。所以我们正在发送pure x &&& at 2to的输出W.until

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

组合使用W.until将产生与事件配对的值的箭头(例如pure x &&& at 2)转换为在事件发生之前产生值的箭头。一旦发生这种情况,我们使用 . 切换到另一根线(-->)

现在应该更容易理解为什么不能使用periodic而不是at. at只发生在一个瞬间,而periodic不断发生,因此W.until永远不会终止。(如果您深入研究源代码,您会发现它的W.until作用类似于折叠事件发生,这与直观的解释相符。)

于 2014-05-17T19:04:20.407 回答