获取 Netwire 程序的输入
Posted
技术标签:
【中文标题】获取 Netwire 程序的输入【英文标题】:Getting input into Netwire programs 【发布时间】:2014-05-16 02:29:49 【问题描述】:我开始使用 Netwire 版本 5。
我可以毫无问题地编写我想将输入转换为输出的所有线路。
现在是时候编写 IO 包装器来绑定我的实际输入了,我有点困惑。
我是否应该为Wire s e m a b
的s
参数创建一个自定义会话类型并将我的传感器值嵌入其中?
如果是这样,我有以下问题:
class (Monoid s, Real t) => HasTime t s | s -> t
的 Monoid s
上下文是怎么回事?它是做什么用的?
我正在考虑用我的传感器读数添加Map String Double
,但我的幺半群应该如何处理字典?应该偏左吗?偏右?以上都不是?
如果没有,我该怎么办?我想以Wire s InhibitionReason Identity () Double
形式的电线结束一些s
,代表我的输入。
据我了解,我不想或不需要为此目的使用 Wire
的单子 m
参数,从而允许电线本身是纯净的,并将 IO 限制在通过顶部的代码中-水平线。这是不正确的吗?
【问题讨论】:
Map 有一个左偏的幺半群实例。而且我实际上认为将这里的单子上下文作为阅读器单子并将您的传感器信息填充在那里是合理的。这并不会真正影响您踩电线的地方,因为您可以runReader
(或者runReaderT
,如果您想要更多的东西)。
那里的教程/示例建议使用内部线路中的 IO 操作来获取传感器数据。例如在其中使用mkGen_
和getKey
。我有兴趣将这个问题概括为:“与将所有 IO 数据作为输入馈送到最外层的线路相比,允许线路内部的 IO 操作有哪些优点和缺点?”
【参考方案1】:
将数据放入Wire s e m a b
的最简单方法是通过输入a
。有可能通过使用WPure
或WGen
从状态增量s
或底层Monad
m
中获取数据,但这些使我们远离主要抽象。主要的抽象是Arrow
和Category
,它们只知道a b
,不知道s e m
。
这是一个非常简单的程序示例,提供输入作为输入a
。 double
是程序的最外层。 repl
是一个小的 read-eval-print 循环,它调用 stepWire
来运行线路。
import FRP.Netwire
import Control.Wire.Core
import Prelude hiding (id, (.))
double :: Arrow a => a [x] [x]
double = arr (\xs -> xs ++ xs)
repl :: Wire (Timed Int ()) e IO String String -> IO ()
repl w = do
a <- getLine
(eb, w') <- stepWire w (Timed 1 ()) (Right a)
putStrLn . either (const "Inhibited") id $ eb
repl w'
main = repl double
请注意,我们将时差传递给stepWire
,而不是总经过时间。我们可以通过运行不同的顶层线来检查这是否正确。
timeString :: (HasTime t s, Show t, Monad m) => Wire s e m a String
timeString = arr show . time
main = repl timeString
哪个有想要的输出:
a
1
b
2
c
3
【讨论】:
【参考方案2】:我刚刚以 Arrow 的方式解决了这个问题,所以这可能更具组合性。喜欢的可以看我的帖子。 Kleisli Arrow in Netwire 5? 和 Console interactivity in Netwire?。第二个帖子有完整的互动程序
首先,你需要这个来提升 Kleisli 函数(即任何东西 a -> m b
):
mkKleisli :: (Monad m, Monoid e) => (a -> m b) -> Wire s e m a b
mkKleisli f = mkGen_ $ \a -> liftM Right $ f a
然后,假设您想从终端获取字符,您可以通过以下方式解除hGetChar
:
inputWire :: Wire s () IO () Char
inputWire = mkKleisli $ \_ -> hGetChar stdin
我还没有测试过这个 runWire
函数(我只是从我之前的帖子中剥离了代码),但它应该运行你的线路:
runWire :: (Monad m) => Session m s -> Wire s e m () () -> m ()
runWire s w = do
(ds, s') <- stepSession s
-- | You don't really care about the () returned
(_, w') <- stepWire w ds (Right ())
runWire s' w'
您可以像任何其他连线或箭头一样在任何您喜欢的位置组合输入连线。在我的示例中,我是这样做的(不要只是复制,程序的其他部分不同):
mainWire = proc _ -> do
c <- inputWire -< ()
q <- quitWire -< c
outputWire -< c
returnA -< q
或者,单行:
mainWire = inputWire >>> (quitWire &&& outputWire) >>> arr (\(q,_) -> q)
【讨论】:
以上是关于获取 Netwire 程序的输入的主要内容,如果未能解决你的问题,请参考以下文章
NetWire RAT通过旧版Microsoft Excel 4.0 宏进行传播