私はNetwire
haskell ライブラリでテストしており、単純な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
次のコードで目標を達成しました。キーを押すとタイマーが停止します。更新 2pollInput
別の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
これは私のオリジナルよりもはるかに優れていると思いますが、それが良い習慣であるかどうかはまだ完全には確信が持てません.