reactive-banana как правильно делать IO события

Основываясь на моем предыдущем вопросе, настройки которого я постепенно уточнял (Как создать монадическое поведение в реактивном банане):

Предположим, что есть eKey, событие, запускаемое при каждом нажатии клавиши, b типа Buffer, которое модифицируется соответствующим образом всякий раз, когда поддерживаемое событие происходит в eKey, и, наконец, есть IO действия, которые необходимо выполнить для некоторых событий. Эти IO действия зависят от состояния b (для простоты предположим, что они выводят текущее состояние b на консоль).

В настоящее время у меня есть это, чтобы выбрать действие, которое происходит в событии:

getKeyAction :: KeyVal -> Maybe (IO Buffer -> IO Buffer)
getKeyAction 65288 = Just . liftM $ backspace
getKeyAction 65293 = Just $ \mb -> do
    b <- mb
    print $ "PRESSED ENTER: " ++ toString b
    return emptyBuffer
getKeyAction 65360 = Just . liftM $ leftMost
getKeyAction 65361 = Just . liftM $ left
...
getKeyAction _     = Nothing

и тогда я делаю:

let
    eBufferActions = filterJust $ getKeyAction <$> eKey
    bBuffer        = accumB (return emptyBuffer) eBufferActions -- model `b`
eBuffer <- changes bBuffer
reactimate' $ fmap displayBuffer <$> eBuffer

для некоторых displayBuffer :: IO Buffer -> IO ().

Похоже, это не работает так, как предполагалось. Состояние bBuffer, по-видимому, заново оценивается для каждого события (фактически выполняются все действия IO, собранные до сих пор, каждый раз, когда происходит событие), что имеет смысл, когда я думаю об этом в ретроспективе.

Как я должен реорганизовать свой код, чтобы сделать это правильно? (т.е. IO действий видят текущее состояние буфера, ничего кроме буфера не накапливается)


Если бы я мог построить Event, который имеет значение bBuffer в случае соответствующего события eKey, тогда я мог бы просто сопоставить свое специальное действие IO с ним и reactimate. Как вы думаете? Как это сделать? Добьется ли <@ того, что я пытаюсь сделать? Но как мне отложить текущее изменение на b, связанное с текущим нажатием клавиши, после того, как я сделаю снимок b с <@, чтобы отобразить Buffer -> IO () поверх него?


person jakubdaniel    schedule 14.10.2015    source источник


Ответы (1)


Итак, я считаю, что это решает мою проблему, но я не уверен, что это правильно. Пожалуйста, прокомментируйте.

  1. Я исключаю те действия, которые выполняют некоторые нетривиальные IO (кроме return)
  2. I filter the eKey event into two: eBuffer and eConfirm
    1. eBuffer collects all modifying events (including clearing the buffer on confirmation)
    2. eConfirm собирает все события подтверждения
  3. Я помечаю eConfirm значениями bBuffer, что отражает эволюцию буфера.
  4. Наконец, я reactimate отдельно IO и changes буфера

Фрагмент кода:

getKeyAction :: KeyVal -> Maybe (Buffer -> Buffer)
getKeyAction 65288 = Just backspace
-- omit action for ENTER
...


getConfirm :: KeyVal -> Maybe (Buffer -> Buffer)
getConfirm 65293 = Just (const mkBuffer) -- Clear buffer on ENTER
getConfirm _     = Nothing

Затем в описании сети:

let
    eBuffer  = filterJust $ getKeyAction <$> eKey
    eConfirm = filterJust $ getConfirm   <$> eKey
    bBuffer  = accumB mkBuffer $ unions [ eBuffer, eConfirm ]
    eEval    = bBuffer <@ eConfirm

eBufferChanges <- changes bBuffer

reactimate  $         evalBuffer <$> eEval
reactimate' $ fmap displayBuffer <$> eBufferChanges

для evalBuffer :: Buffer -> IO () и displayBuffer :: Buffer -> IO ().

person jakubdaniel    schedule 14.10.2015
comment
Выглядит хорошо для меня. Шаг 1 — ключ ко всему. Тем не менее, я немного запутался в ваших соглашениях об именах. Почему вы называете это evalBuffer? Насколько я понимаю, Buffer — это неизменяемое значение, которое вы меняете, применяя чистые функции Buffer -> Buffer. - person Heinrich Apfelmus; 15.10.2015
comment
evalBuffer — это действие IO, которое принимает текущее состояние Buffer и оценивает его (отправляет буфер интерпретатору и выводит результат на консоль). Если вы имели в виду eEval, то это события, требующие оценки. Я лично не поддерживаю это название, это была просто первая идея, которая у меня возникла. - person jakubdaniel; 15.10.2015