Получение ввода в программы Netwire

Я начинаю работу с Netwire версии 5.

У меня нет проблем с записью всех проводов, которые я хочу преобразовать входы в мои выходы.

Теперь пришло время написать обертку IO для привязки к моим реальным входам, и я немного смущен.

Предполагаю ли я создать пользовательский тип сеанса для параметра s Wire s e m a b и встроить в него значения моего датчика?

Если да, у меня есть следующие вопросы:

  • Что связано с Monoid s контекстом class (Monoid s, Real t) => HasTime t s | s -> t? Для чего он используется?
  • Я думал о том, чтобы привязать Map String Double к моим показаниям датчика, но как должен мой моноид хрустнуть словари? Должна ли она быть предвзятой? Право предвзятым? Ничего из перечисленного?

Если нет, что я должен делать? Я хочу получить провода формы Wire s InhibitionReason Identity () Double для некоторого s, представляющего мой ввод.

Я понимаю, что для этой цели я не хочу или не хочу использовать монадический параметр m Wire, позволяя самим проводам быть чистыми и ограничивать IO кодом, который проходит через верхний уровень, (ы). Это неверно?

Ответ 1

Самый простой способ поместить данные в Wire s e m a b - через вход a. Возможно, используя WPure или WGen, чтобы получить данные из дельта состояния s или базового Monad m, но они отвлекают нас от основных абстракций. Основные абстракции Arrow и Category, которые знают только о a b, а не о s e m.

Здесь приведен пример очень простой программы, обеспечивающей ввод как входной сигнал a. double - самый внешний провод программы. repl представляет собой небольшой цикл чтения-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

Я просто решил это со стрелкой, так что это может быть более композиционно. Вы можете прочитать мои сообщения, если хотите. Kleisli Arrow в Netwire 5? и Интерактивность консоли в 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)