Haskell Netwire:正确完成电线

我正在尝试在netwire 5“正确”中实现一组动态的电线.

我已经阅读了
wires of wires问题的答案,我并不特别喜欢这个例子中的代码如何依赖于转换为一个行为的事件,以便在一次执行stepWire时显示非空.

所以,我想通过事件添加和删除动态集中的连线,并希望不会触及Unsafe.Event或等效的hackery.为简单起见,让我们删除删除部分,只需添加Wires即可:

dynWireSet1 :: (Monad m, Monoid s)
            => Wire s e m (a, Event (Wire s e m a b)) [b]

每个事件都会向隐藏在其中的(最初为空的)列表(或其他集合)添加一条新线路,并且它们都会运行,所有都获得类型a的输入,并将其输出收集到列表中.

运行部分相对容易,有可谷歌的例子,例如:

dynWireSet1 = runWires1 []
runWires1 :: (Monad m, Monoid s)
          => [Wire s e m a b]
          -> Wire s e m (a, Event (Wire s e m a b)) [b]
runWires1 wires = mkGen $\session (input, event) -> do
  stepped <- mapM (\w -> stepWire w session (Right input)) wires
  let (outputs, newwires) = unzip stepped
  return (sequence outputs, runWires1 newwires)

上面的示例忽略了事件.我怀疑这是不可能的
在转换函数中使用事件,而不是使用
来自Unsafe.Event的事件函数.那是对的吗?一世
想要避免Unsafe.Event.

当我退后一步看看建议的使用事件的方法时,我看到了一个
功能看起来非常有希望:

krSwitch :: Monad m
         => Wire s e m a b
         -> Wire s e m (a, Event (Wire s e m a b -> Wire s e m a b)) b

现在,如果我从简化的runWires开始怎么办?

runWires2 :: (Monad m, Monoid s)
          => [Wire s e m a b]
          -> Wire s e m a [b]
runWires2 wires = mkGen $\session input -> do
  stepped <- mapM (\w -> stepWire w session (Right input)) wires
  let (outputs, newwires) = unzip stepped
  return (sequence outputs, runWires2 newwires)

并使dynWireSet成为krSwitch:

dynWireSet2 :: (Monad m, Monoid s)
            => Wire s e m (a, Event (Wire s e m a b)) [b]
dynWireSet2 = krSwitch (runWires2 []) . second (mkSF_ (fmap addWire))
addWire :: Wire s e m a b -> Wire s e m a [b] -> Wire s e m a [b]
addWire = undefined

我快到了!现在,如果我只能通过runWires2 fmap a(:)并将新线插入到newwires中,我就会全部设置好!但在一般情况下,这是不可能的.实际上,如果我说得对,那么fG over WGen就会对输出进行fmaps.无用.

现在,这是我的想法.让我们介绍一种新的数据变量Wire,我暂时将其称为WCarry g st,因为它将以不同的数据类型携带其内部状态.它的过渡功能将属于该类型

((a, c) -> m (b, c))

并且,在初始状态下,构造函数将生成如下所示的Wire:

mkCarry :: Monad m => ((a, c) -> m (b, c)) -> c -> Wire s e m a b
mkCarry transfun state = mkGenN $\input -> do
  (output, newstate) <- transfun (input, state)
  return (Right output, mkCarry transfun newstate)

只将WCarry类型而不是WGen类型引入到生成的导线中.在mkCarry方面很容易重新制定runWires.

然后,fmap实例看起来像这样:

fmap f (WCarry g st) = WCarry g (fmap f st)

它将改变“隐藏的内部”状态对象,并且我们将能够在这种类型的Wires上有意义地使用krSwitch函数,以调整其内部状态而不会丢失先前的值.

这有意义吗?如果我想以更简单的方式做,我们建议!如果我所说的是有道理的,我怎么能去做呢?是否可以使用WCarry在本地扩展数据Wire定义,并使用相应的定义扩展添加有趣的Class实例?还有其他建议吗?

谢谢.

最佳答案 我在使用Netwire时遇到了同样的问题,所以我认为回答这个问题会很有用.我同意使用(安全)事件是正确的方法.但是我不喜欢添加WCarry,它似乎不太直观.

你实际上非常接近答案.使addWire的关键在于你不想“修改”旧线.你想要的是创建一个添加了给定子线的输出的新线,所以这可能是你正在寻找的:

addWire w ws = fmap (uncurry (:)) (w &&& ws)

该导线馈送两根导线,然后连接输出.希望能帮助到你!

点赞