Поиск…


Внедрение внешних событий в библиотеку

Этот пример не привязан к какому-либо конкретному набору инструментов GUI, например, реактивный банан-wx. Вместо этого он показывает, как вводить арбитационные операции IO в машины FRP.

Модуль Control.Event.Handler предоставляет функцию addHandler которая создает пару значений AddHandler a и a -> IO () . Первый используется самим реактивным бананом, чтобы получить значение Event a , а последнее - это простая функция, которая используется для запуска соответствующего события.

import Data.Char (toUpper)

import Control.Event.Handler
import Reactive.Banana

main = do
    (inputHandler, inputFire) <- newAddHandler

В нашем случае параметр обработчика имеет тип a String , , но код , который позволяет компилятору сделать вывод , что будет написано позже.

Теперь мы определяем EventNetwork которая описывает нашу систему, основанную на FRP. Это делается с помощью функции compile :

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        inputEvent <- fromAddHandler inputHandler

Функция fromAddHandler преобразует значение AddHandler a в Event a , которое рассматривается в следующем примере.

Наконец, мы запускаем наш «цикл событий», который запускает события на входе пользователя:

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        ...
    forever $ do
        input <- getLine
        inputFire input

Тип события

В реактивном банане тип Event представляет собой поток некоторых событий во времени. Event похоже на аналоговый импульсный сигнал в том смысле, что он не является непрерывным во времени. В результате Event является экземпляром Functor класса типов только. Вы не можете объединить два Event вместе, потому что они могут срабатывать в разное время. Вы можете сделать что-то со значением [current] Event и отреагировать на него с помощью некоторых IO .

Преобразования по значению Event s выполняются с использованием fmap :

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        inputEvent <- fromAddHandler inputHandler
        -- turn all characters in the signal to upper case
        let inputEvent' = fmap (map toUpper) inputEvent

Реагирование на Event осуществляется таким же образом. Сначала вы fmap его с помощью действия типа a -> IO () а затем передадите его в функцию reactimate :

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        inputEvent <- fromAddHandler inputHandler
        -- turn all characters in the signal to upper case
        let inputEvent' = fmap (map toUpper) inputEvent
        let inputEventReaction = fmap putStrLn inputEvent' -- this has type `Event (IO ())
        reactimate inputEventReaction

Теперь , когда inputFire "something" - "SOMETHING" inputFire "something" называется, "SOMETHING" будет напечатано.

Тип поведения

Чтобы представить непрерывные сигналы, реакционно-банановые функции Behavior a типа. В отличие от Event , Behavior является Applicative , которое позволяет комбинировать n Behavior s с помощью n-ary чистой функции (используя <$> и <*> ).

Чтобы получить Behavior a от Event a существует функция accumE :

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        ...
        inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent

accumE принимает начальное значение Behavior и Event , содержащее функцию, которая устанавливает ее в новое значение.

Как и в случае с Event s, вы можете использовать fmap для работы с текущим значением Behavior s, но вы также можете комбинировать их с (<*>) .

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        ...
        inputBehavior  <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
        inputBehavior' <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
        let constantTrueBehavior = (==) <$> inputBehavior <*> inputBehavior'

Чтобы реагировать на изменения Behavior есть функция changes :

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        ...
        inputBehavior  <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
        inputBehavior' <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
        let constantTrueBehavior = (==) <$> inputBehavior <*> inputBehavior'
        inputChanged <- changes inputBehavior

Единственное, что следует отметить, это то, что changes возвращают Event (Future a) вместо Event a . Из-за этого reactimate' следует использовать вместо того, чтобы reactimate . Обоснование этого можно получить из документации.

Приведение в действие EventNetworks

EventNetwork s, возвращаемая compile должна быть активирована до того, как активируются события, связанные с реакцией.

main = do
    (inputHandler, inputFire) <- newAddHandler

    eventNetwork <- compile $ do
        inputEvent <- fromAddHandler inputHandler
        let inputEventReaction = fmap putStrLn inputEvent
        reactimate inputEventReaction

    inputFire "This will NOT be printed to the console!"
    actuate eventNetwork
    inputFire "This WILL be printed to the console!"


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow