Haskell Language
반응 - 바나나
수색…
외부 이벤트를 라이브러리에 삽입
이 예제는 예를 들어 reactive-banana-wx와 같은 구체적인 GUI 툴킷과 관련이 없습니다. 대신에 임의의 IO 작업을 FRP 기계에 주입하는 방법을 보여줍니다.
Control.Event.Handler 모듈은 AddHandler a 및 a -> IO () 값 쌍을 만드는 addHandler 함수를 제공합니다. 전자는 반응식 바나나 자체에서 Event a 값을 가져 오는 데 사용되는 반면 후자는 해당 이벤트를 트리거하는 데 사용되는 일반 함수입니다.
import Data.Char (toUpper)
import Control.Event.Handler
import Reactive.Banana
main = do
(inputHandler, inputFire) <- newAddHandler
이 경우 처리기 a 매개 변수는 String 유형이지만 컴파일러에서 추론 할 수있는 코드는 나중에 작성됩니다.
이제 FRP 기반 시스템을 설명하는 EventNetwork 를 정의합니다. 이것은 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 typeclass의 인스턴스입니다. 서로 다른 시간에 발사 할 수 있기 때문에 두 개의 Event 함께 결합 할 수 없습니다. 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 반응하는 것도 같은 방식으로 수행됩니다. 먼저 a -> IO () 유형의 액션으로 fmap 을 수행 한 다음 함수를 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" 이 인쇄됩니다.
행동 유형
지속적인 신호를 표현하기 위해 반응성 바나나 기능 Behavior a 유형. Event 와 달리 Behavior 은 Applicative 이며 n Behavior 순수 함수 ( <$> 및 <*> )를 사용하여 n Behavior 를 결합 할 수 있습니다.
Event a 에서 Behavior a 를 얻으려면 다음과 같이 accumE 함수가 있습니다.
main = do
(inputHandler, inputFire) <- newAddHandler
compile $ do
...
inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
accumE 는 Behavior 의 초기 값과 Event 에 새로운 값을 설정하는 함수를 포함합니다.
Event 와 마찬가지로 fmap 을 사용하여 현재 Behavior 값으로 작업 할 수 있지만 (<*>) 와 결합 할 수도 있습니다.
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 a 대신 Event (Future a) 를 반환한다는 것입니다. 이 때문에 반응을 reactimate' 대신에 reactimate 을 reactimate 합니다. 이것에 대한 근거는 문서에서 얻을 수 있습니다.
이벤트 네트워크 작동
EventNetwork 이벤트가 효과를 내기 전에 compile 의해 리턴 된 EventNetwork 가 작동되어야합니다.
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!"