我正在使用 haskell 库进行测试,并使用简单的电线Netwire
使其工作:time
import Control.Wire
import Prelude hiding ((.), id)
import Control.Monad.IO.Class
import Data.Functor.Identity
import System.IO
wire :: (HasTime t s) => Wire s () m a t
wire = time
run :: (HasTime t s, MonadIO m, Show b, Show e) =>
Session m s -> Wire s e m a b -> m ()
run session wire = do
(dt, session') <- stepSession session
(wt', wire') <- stepWire wire dt $ Right undefined
case wt' of
-- | Exit
Left _ -> return ()
Right x -> do
liftIO $ do
putChar '\r'
putStr $ either (\ex -> show ex) show wt'
hFlush stdout
-- Interactivity here?
gotInput <- hReady stdin
if gotInput then
return ()
else return ()
run session' wire'
main :: IO ()
-- main = testWire clockSession_ wire
main = run clockSession_ wire
注意:run
基本上是从 修改的testWire
,所以我不知道它是否是形成电线网络的正确方法。部分代码来自http://todayincode.tumblr.com/post/96914679355/almost-a-netwire-5-tutorial但该教程并未提及事件。
现在我正在尝试为程序添加一些交互性。现在,当按下任意键时退出程序。我想我应该做一些事件切换。但是,我被困在这里,因为我找不到改变wire'
或切换行为的方法。我试图阅读 API 文档和源代码,但我看不到如何实际“触发”事件或使用它来切换线路。
同样,由于我对 Haskell 还不是很熟悉,我可能在这里犯了一些愚蠢的错误。
更新 1/2
我通过以下代码实现了我的目标。计时器在任何按键时停止。更新 2我设法分离出pollInput
另一个IO
唯一的功能,耶!
import Control.Wire
import Prelude hiding ((.), id)
import Control.Monad.IO.Class
import Data.Functor.Identity
import System.IO
wire :: (HasTime t s) => Wire s () m a t
wire = time
run :: (HasTime t s, MonadIO m, Show b, Show e) =>
Session m s -> Wire s e m a b -> m ()
run session wire = do
-- Get input here
input <- liftIO $ pollInput
(dt, session') <- stepSession session
(wt', wire') <- stepWire wire dt $ input
case wt' of
-- | Exit
Left _ -> liftIO (putStrLn "") >> return ()
Right x -> do
liftIO $ do
putChar '\r'
putStr $ either (\ex -> show ex) show wt'
hFlush stdout
run session' wire'
pollInput :: IO (Either a b)
pollInput = do
gotInput <- hReady stdin
if gotInput then
return (Left undefined)
else return (Right undefined)
setup :: IO ()
setup = do
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
main :: IO ()
main = do
setup
run clockSession_ wire
然而,这引发了一些进一步的问题。首先,这是好的做法吗?第二,什么是类型pollInput
?我试图手动输入它但没有成功。但是,自动类型推断有效。
这是我对这段代码如何工作的解释:
首先,轮询来自控制台的用户输入,经过一些逻辑后,生成到线的“输入”(名称选择不佳,但生成的输入是线输入)并沿网络传递。在这里,我简单地传递了一个抑制(Left something
),并导致循环退出。当然,退出时,程序会生成一个换行符以使控制台看起来更好。
(好吧,我仍然不明白它是如何Event
工作的)
更新 3/4
在阅读了@Cirdec 的答案并在我的编辑器上摆弄了很多之后,我得到了这个没有 的单线程版本IORef
,也退出了按 'x' Update 4:(但它不输出任何东西):
import Control.Wire
import Prelude hiding ((.),id)
import Control.Wire.Unsafe.Event
import System.IO
import Control.Monad.IO.Class
data InputEvent = KeyPressed Char
| NoKeyPressed
deriving (Ord, Eq, Read, Show)
type OutputEvent = IO ()
--- Wires
example :: (HasTime t s, Monad m, Show t) =>
Wire s () m (Event [InputEvent]) (Event [OutputEvent])
example = switch $
(fmap ((:[]) . print) <$> periodic 1 . time
&&&
fmap (const mkEmpty) <$> filterE (any (== KeyPressed 'x'))
)
readKeyboard :: IO (Either e (InputEvent))
readKeyboard = do
hSetBuffering stdin NoBuffering
gotInput <- hReady stdin
if gotInput then do
c <- getChar
return $ Right $ KeyPressed c
else return $ Right $ NoKeyPressed
output :: [OutputEvent] -> IO ()
output (x:xs) = id x >> output xs
output _ = return ()
run :: (HasTime t s, MonadIO m) =>
Session m s -> Wire s e m (Event [InputEvent]) (Event [OutputEvent]) -> m e
run = go
where
go session wire = do
-- | inputEvent :: Event InputEvent
inputEvent <- liftIO $ readKeyboard
(dt, session') <- stepSession session
(wt', wire') <- stepWire wire dt (Event <$> (fmap (:[]) inputEvent))
-- (wt', wire') <- stepWire wire dt (Right undefined)
case wt' of
Left a -> return a
Right bEvent -> do
case bEvent of
Event b -> liftIO $ output b
_ -> return ()
go session' wire'
main = do
run clockSession_ example
我认为这比我原来的要好得多,但我仍然不完全相信它是否是好的做法。