Haskell 使用netwire';s周期与at导线

Haskell 使用netwire';s周期与at导线,haskell,functional-programming,frp,netwire,Haskell,Functional Programming,Frp,Netwire,我正在尝试用Haskell编写一个实时交互式图形框架。我一直试图通过使用Netwire 5来处理问题,但我似乎无法很好地处理事物之间如何“相互依赖”,以下代码应在切换到(val+1)之前产生两秒钟的val,然后无限期地继续 someWire :: Num a => a -> Wire s e m a a someWire x = (-->) ((pure x) &&& (periodic 2) >>> until) (someWire

我正在尝试用Haskell编写一个实时交互式图形框架。我一直试图通过使用Netwire 5来处理问题,但我似乎无法很好地处理事物之间如何“相互依赖”,以下代码应在切换到(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
开始计数,每两秒更改一次值。有人能解释一下这种行为吗?

让我们看看工作版本:

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 
Wire
s是
Arrow
s,因此如果我们能够遵循Arrow组合器所做的操作,就更容易理解正在发生的事情

(&&&) :: 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
类别
的一个子类),先写后写。因此,我们将
纯x&&at 2的输出发送到
W,直到

W.until :: Monoid e => Wire s e m (a, Event b) a
使用
W.until
组合将生成与事件成对的值的箭头(例如
pure x&&at 2
)转换为生成值直到事件发生的箭头。一旦发生这种情况,我们就使用
(->)
切换到另一条线路


现在应该更容易理解为什么不能在
处使用
定期
而不是
at
仅在一个瞬间发生,而
周期性的
持续发生,因此
W直到
永不终止。(如果你翻查来源,你会发现,
W.until
做了类似于折叠事件发生的事情,这与直观的解释相匹配。)

关键洞察是,
周期性的
会立即产生事件

因此,当我们要从该导线产生一个值时,我们必须将其评估为以下内容:

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

由于这不是尾部递归,因此不允许垃圾收集器清理之前分配给wire的实例,因此内存不足(而不是获得无限循环)。

您的
someWire
s不进行类型检查。我想你的意思是,例如,
((纯val&&&&(周期2))>>>>>直到-->(someWire(val+1))
。谢谢,我把它修好了。这里有一个更好的完全崩溃的例子:人力资源管理,这对我来说仍然没有意义。如果我用
periodic 2>>(delay NoEvent)
替换
periodic 2
,那么一切都会按照我的预期运行(它每隔一个接线步骤切换一次)。如文档所述,
W.until
所做的“折叠”仅用于在事件发生时消耗事件的值。我相信内存消耗与我不理解的电线的时空依赖性有关…+1你的问题比我最初想的要微妙得多。