【问题标题】:Haskell Netwire: wires of wires done properlyHaskell Netwire:正确完成的电线
【发布时间】:2015-10-14 15:33:31
【问题描述】:

我正在尝试“正确”地在 netwire 5 中实现一组动态电线。 我已经阅读了wires of wires 问题的答案,我并不特别喜欢示例中的代码如何依赖Event 转换为在恰好一次执行时显示非空的行为 em> stepWire

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

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

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

运行部分比较简单,有googleable的例子,例如:

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.Eventevent 函数。那是对的吗?我 想避开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(:) 并将新线插入newwires,我就准备好了!但这在一般情况下是不可能的。事实上,fmap 超过 WGen 只是 fmaps 超过输出,如果我没猜错的话。没用。

现在,这是我的想法。让我们介绍一个data 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 在本地扩展 data Wire 定义,并扩展添加具有相应定义的有趣 Class 实例?还有其他建议吗?

谢谢。

【问题讨论】:

    标签: haskell netwire


    【解决方案1】:

    我在使用 Netwire 时遇到了完全相同的问题,所以我认为回答这个问题会很有用。我同意使用(安全)事件是正确的方法。不过我不喜欢加WCarry,看起来不太直观。

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

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

    这条线为两条线供电,然后连接输出。希望对您有所帮助!

    【讨论】:

    • 它适用于组合现有集的输出和新线。但在下一步中,我显然想要移除集合中的电线(使用例如地图进行表示)。我可以按照您的建议删除不需要的输出,但这不会导致内存泄漏吗?
    • 评论自己的评论,也许使用惰性映射可以缓解内存泄漏问题?会吗?
    • 当你想移除w时,你可以让w &amp;&amp;&amp; ws响应一个切换回ws的事件。
    猜你喜欢
    • 2015-05-09
    • 2013-09-02
    • 2013-12-18
    • 2018-07-29
    • 2015-01-19
    • 2011-03-11
    • 1970-01-01
    • 2012-07-07
    • 2014-07-06
    相关资源
    最近更新 更多