Может ли реактивный банан обрабатывать циклы в сети? - PullRequest
15 голосов
/ 21 октября 2011

У нас есть код, подобный следующему:

 guiState :: Discrete GuiState
 guiState = stepperD (GuiState []) $
   union (mkGuiState <$> changes model) evtAutoLayout

 evtAutoLayout :: Event GuiState
 evtAutoLayout = fmap fromJust . filterE isJust . fmap autoLayout $ changes guiState

Вы можете видеть, что evtAutoLayout подается в guiState, который подается в evtAutoLayout - так что там есть цикл.Это намеренно.Автоматическая разметка регулирует состояние графического интерфейса до тех пор, пока оно не достигнет равновесия, а затем ничего не вернется, поэтому он должен остановить цикл.Разумеется, новое изменение модели может запустить его снова.

Однако, когда мы собираем это вместе, мы сталкиваемся с бесконечным циклом при вызове функции компиляции.Даже если autoLayout = Nothing, это все равно приводит к переполнению стека во время компиляции.

Если я удаляю вызов union в guiState и удаляю evtAutoLayout из рисунка ...

 guiState :: Discrete GuiState
 guiState = stepperD (GuiState []) $ mkGuiState <$> changes model

itотлично работает.

Есть предложения?

1 Ответ

15 голосов
/ 21 октября 2011

Вопрос

Поддерживает ли библиотека реактивного банана рекурсивно определенные события?

имеет не только один, но и три ответа.Краткие ответы: 1. обычно нет, 2. иногда да, 3. с обходным решением да.

Здесь длинные ответы.

  1. Семантика реактивного банана не поддерживает определение Event непосредственно в терминах самого себя .

    Это решение было принято Коналом Эллиоттом в его оригинальной семантике FRP, и я решилпридерживаться этого.Его главное преимущество заключается в том, что семантика остается очень простой, вы всегда можете думать с точки зрения

    type Behavior a = Time -> a
    type Event    a = [(Time,a)]
    

    Я предоставил модуль Reactive.Banana.Model , который реализует почти точно эту модель,Вы можете обратиться к его исходному коду для любых вопросов, касающихся семантики реактивного банана.В частности, вы можете использовать его для рассуждения о своем примере: расчет с ручкой и бумагой или попытка его в GHCi (с некоторыми ложными данными) скажет вам, что значение evtAutoLayout равно _|_, то есть не определено.

    Последнее может удивлять, но, как вы написали, пример действительно не определен: состояние GUI изменяется только в случае события evtAutoLayout, но это может произойти, только если вы знаете, изменяется ли состояние GUI,который в свою очередь и т. д. Вы всегда должны разорвать петлю обратной связи удушения с помощью , вставив небольшую задержку .К сожалению, реактивный банан в настоящее время не предлагает способ вставки небольших задержек, главным образом потому, что я не знаю, как описать небольшие задержки в терминах модели [(Time,a)] таким образом, чтобы обеспечить рекурсию.(Но см. Ответ 3.)

  2. Можно и рекомендуется определить Event в терминах Behavior, который снова относится к Событию.Другими словами, рекурсия разрешена, пока вы проходите через Поведение.

    Простой пример будет

    import Reactive.Banana.Model
    
    filterRising :: (FRP f, Ord a) => Event f a -> Event f a
    filterRising eInput = eOutput
        where
        eOutput  = filterApply (greater <$> behavior) eInput
        behavior = stepper Nothing (Just <$> eOutput)
    
        greater Nothing  _ = True
        greater (Just x) y = x < y
    
    example :: [(Time,Int)]
    example = interpretTime filterRising $ zip [1..] [2,1,5,4,8,9,7]
    -- example = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]
    

    При наличии потока событий функция filterRising возвращает только эти событиякоторые больше, чем ранее возвращенные.На это намекает документация для функции stepper .

    Однако, вероятно, это не тот тип рекурсии, который вам нужен.

  3. Тем не менее, можно добавить небольшие задержки в реактивном банане, он просто не является частью базовой библиотеки и, следовательно, не имеет какой-либо гарантированной семантики.Кроме того, вам нужна некоторая поддержка из цикла событий, чтобы сделать это.

    Например, вы можете использовать wxTimer, чтобы запланировать событие, которое произойдет сразу после обработки текущего события.Пример Wave.hs демонстрирует рекурсивное использование wxTimer с реактивным бананом.Я не совсем знаю, что происходит, когда вы устанавливаете интервал таймера на 0, хотя он может выполняться слишком рано.Возможно, вам придется немного поэкспериментировать, чтобы найти хорошее решение.

Надеюсь, это поможет;Не стесняйтесь спрашивать разъяснения, примеры и т. д.

Раскрытие информации: я являюсь автором библиотеки реактивных бананов.

...