Получение ввода в программы 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)