¿Interactividad de consola en Netwire?

Estoy probando con elNetwire biblioteca Haskell y lo hizo funcionar con un simpletime cable:

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

Nota larun se modifica básicamente detestWire, así que no sé si es la forma correcta de formar una red de cables. Parte del código de origen dehttp://todayincode.tumblr.com/post/96914679355/almost-a-netwire-5-tutorial pero ese tutorial no dice sobre eventos.

Ahora estoy tratando de agregar un poco de interactividad al programa. Por ahora, salga del programa cuando se presione cualquier tecla. Supongo que debería hacer algún cambio de evento. Sin embargo, estoy atrapado aquí porque no puedo encontrar una manera de cambiarwire' o cambiar el comportamiento. Traté de leer el documento API y la fuente, pero no veo cómo "disparar" un evento o usarlo para cambiar el cable.

Nuevamente, dado que aún no estoy muy familiarizado con Haskell, es posible que haya cometido algunos errores estúpidos aquí.

Actualización 1/2

Obtuve mi objetivo trabajando con el siguiente código. El temporizador se detiene al presionar cualquier tecla.Actualización 2 Logré separarmepollInput en otroIO única función, ¡Yay!

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

Sin embargo, esto plantea algunas preguntas más. Primero, ¿es esta una buena práctica?Segundo, ¿cuál es el tipo depollInput? Traté de escribirlo manualmente pero sin éxito. Sin embargo, la deducción automática de tipos funciona.

Esta es mi explicación de cómo funciona este código:

Primero, se sondea la entrada del usuario desde la consola, y después de cierta lógica, se genera la "entrada" al cable (elección de nombre deficiente, pero esa entrada generada es la entrada del cable) y se pasa a lo largo de la red. Aquí, simplemente paso una inhibición (Left something), y hará que el bucle salga. Por supuesto, al salir, el programa produce una nueva línea para hacer que la consola se vea mejor.

(Bueno, todavía no entiendo cómoEvent funciona, sin embargo)

Actualización 3/4

Después de leer la respuesta de @Cirdec y juguetear mucho con mi editor, obtengo esta versión única sin hilosIORef, también dejando de presionar 'x'Actualización 4: (pero no genera nada):

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) <
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
gt; periodic 1 . time &&& fmap (const mkEmpty) <
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
gt; 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 <
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
gt; (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

Creo que esto es mucho mejor que mi original, pero todavía no estoy completamente convencido de si es una buena práctica o no.

Respuestas a la pregunta(2)

Su respuesta a la pregunta