Haskell Language
Реактивный-банан
Поиск…
Внедрение внешних событий в библиотеку
Этот пример не привязан к какому-либо конкретному набору инструментов 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!"