2014-04-20 2 views
2

이벤트로 교환 할 수있는 오브젝트 스택을 구현하고 싶습니다. 위 또는 아래의 이벤트 양식을 수신하면 오브젝트는 다른 이벤트를 ether 측으로 보내거나 상태를 변경할 수 있습니다 (그러나 스택에서 해당 위치 유지).이벤트 핸들러 스택

현재이 작업이 있습니다. 유형 Animation a ba 유형의 이벤트를 수신하고 유형이 b 인 (위로) 이벤트를 발생시키고 해당 이벤트를 쌓아 올리는 함수 handle :: Handler -> Animation g h -> Animation e f이있는 이벤트 처리기의 컨테이너입니다.

Handler의 실제 타입이 이벤트의 위 또는 아래에 오는 다음

Animation g h -> (Either e h) -> 
    WriterT [Either g f] IO (Either (Animation e f) (Animation g h)) 

(Either e h)이다, [Either g f] 이벤트 아래 출사 또는 위 Either (Animation e f) (Animation g h)는 결과이다 중 어느 하나를 사용하는 독립 객체 또는 하나로 동일한 처리기. 그리고 나는 행복하지 않다.

더 세련된 방법이 있습니까?

+0

'애니메이션'유형은'pipes' 또는'conduit' 라이브러리가 제공하는 것과 비슷합니다. – cdk

+0

@cdk, 잘 알지는 못하지만, 둘 다 데이터 변환에 방해를받는 것 같습니다. 중요한 것은 각 애니메이션이 스스로를 대체 할 수 있어야한다는 것입니다. –

답변

6

정확히 Proxy 유형의 pipes입니다. 도식적으로 다음과 같이 보입니다.

Upstream | Downstream 
    +---------+ 
    |   | 
a' <==  <== b' 
    |   | 
a ==>  ==> b 
    | | | 
    +----|----+ 
      v 
      r 

이 인터페이스에는 업스트림 인터페이스와 다운 스트림 인터페이스라는 두 개의 인터페이스가 있습니다. 두 인터페이스에 대한 정보를 보내고받습니다. 이는 스택의 계층과 유사합니다. "업스트림"이 스택 프레임 위에있을 수 있습니다. "다운 스트림"은 그 아래의 스택 프레임 일 수 있습니다. 즉

request :: Monad m => a' -> Proxy a' a b' b m a 

, request 유형 a의 응답 유형 a' 업스트림 및 대기의 값을 보냅니다

상류 인터페이스와 통신하려면이 유형이있는 request를 사용합니다.

request의 이중

다운 스트림 인터페이스에 통신하는, respond이다

respond :: Monad m => b -> Proxy a' a b' b m b' 

respondb'의 응답 타입 b 하류 대기의 값을 보낸다.

Proxy은 세 가지 상태 중 하나 일 수 있습니다.그것은있을 수 있습니다 :

  • 상류

그것의 종류의 응답을 기다리는 것 그것이 a 기다리고 있음을 나타냅니다 :

waitingUp :: a -> Proxy a' a b' b m r 
  • 다운 스트림로부터의 응답을 기다리는 :

유형 w 울드 그것이 b' 기다리고 있음을 나타냅니다

waitingDn :: b' -> Proxy a' a b' b m r 
  • 현재 활동하고 아무것도 기다리지 않고 :

그것의 유형은 모든 값이 기다리고 아니에요 것을 나타냅니다 :

notWaiting :: Proxy a' a b' b m r 

다음 세 가지 상태를 연결할 수있는 방법은 네 가지가 있습니다.

  • 다운 스트림에서 대기중인 Proxy을 활성 Proxy에 연결하면 새로운 활성 Proxy이 생성됩니다.

이것은 (+>>) 운영자가하는 일입니다 :

(+>>) 
    :: Monad m 
    => (b' -> Proxy a' a b' b m r) -- Waiting on downstream 
    ->  Proxy b' b c' c m r -- Active 
    ->  Proxy a' a c' c m r -- Active 
  • 새로운 활성 Proxy 생성 상류에 대기 Proxy에 활성 Proxy를 연결합니다.

이것은 (>>~) 운영자가하는 일입니다 : 상류에서 대기중인

(>>~) 
    :: Monad m 
    =>  Proxy a' a b' b m r -- Active 
    -> (b -> Proxy b' b c' c m r) -- Waiting on upstream 
    ->  Proxy a' a c' c m r -- Active 
  • 연결이 개 Proxy들, 상류에서 대기 새로운 Proxy을 생성 할 수 있습니다.

이것은 (>~>) 운영자가하는 일입니다 : 모두 다운 스트림에서 대기중인

(>~>) 
    :: Monad m 
    => (a -> Proxy a' a b' b m r) -- Waiting on upstream 
    -> (b -> Proxy b' b c' c m r) -- Waiting on upstream 
    -> (a -> Proxy a' a c' c m r) -- Waiting on upstream 
  • 연결이 개 Proxy들, 다운 스트림을 기다리는 새로운 Proxy을 생성 할 수 있습니다.

이것은 (>+>) 운전자가 무엇입니다

(>+>) 
    :: Monad m 
    => (b' -> Proxy a' a b' b m r) -- Waiting on downstream 
    -> (c' -> Proxy b' b c' c m r) -- Waiting on downstream 
    -> (c' -> Proxy a' a c' c m r) -- Waiting on downstream 

여기 구현이 방법 연결된 세 개의 스택 프레임의 예이다.나는 구현이 완전히 대칭이지만, 스택 상류에서 시작하는 규칙을 사용하고 당신이 원하는 경우 반대 규칙을 사용할 수 있습니다

import Pipes.Core 
import Pipes 

--    +-+-- Closed upstream interface 
--    | | 
--    v v 
up ::() -> Proxy X() String Int IO() 
up() = do 
    str1 <- respond 4 
    lift (putStrLn str1) 
    str2 <- respond 5 
    lift (putStrLn str2) 

middle :: Int -> Proxy String Int Double Char IO() 
middle int = do 
    lift (print int) 
    double <- respond (head (show int)) 
    lift (print double) 
    int' <- request (show double) 
    middle int' 

-- Closed downstream interface --+-+ 
--        | | 
--        v v 
down :: Char -> Proxy Double Char() X IO() 
down char1 = do 
    lift (print char1) 
    char2 <- request (1.0) 
    lift (print char2) 
    char3 <- request (2.0) 
    lift (print char3) 

--     +-+--+--+-- Everything closed 
--     | | | | 
--     v v v v 
total ::() -> Proxy X()() X IO() 
total = up >~> middle >~> down 

main :: IO() 
main = runEffect $ total() 

이 다음과 같은 출력이 생성

>>> main 
4 
'4' 
1.0 
1.0 
5 
'5' 
2.0 
2.0 

시도를 upProxy부터 시작하여 손으로 실행 경로를 추적합니다. 값이 uprespond 인 값이 0이 될 때마다 제어는 middle으로, 매번 middle은이되고 제어 값은 down이됩니다. 반대로 downrequest 값이 0이 될 때마다 제어는 middle으로, 매번 middlerequestup으로 제어되지 않습니다. 체인의 파이프가 종료되면 전체 체인이 종료됩니다.

편집 : 질문에 대답하려면 네 결과에 따라 동작을 변경할 수 있습니다. foobar가 동일한 입력과 출력을 모두 Proxy들입니다

middle :: Int -> Proxy String Int Double Char IO() 
middle int = do 
    lift (print int) 
    double <- respond (head (show int)) 
    case double of 
     0.0 -> foo 
     _ -> bar 

... middle 등 :

foo :: Proxy String Int Double Char IO() 

bar :: Proxy String Int Double Char IO() 

Proxy의 염기 서열을 때, 두 번째 Proxy 그냥 이런 middle 쓰기 첫 번째 Proxy이 끝나는 곳에서 시작됩니다. requestrespond과 같은 기본 명령 시퀀싱에 국한되지 않습니다. 동일한 업스트림 및 다운 스트림 인터페이스를 공유하는 한 Proxy 범위 내에서 서브 루틴으로 임의의 단계 수를 가진 Proxy을 호출 할 수 있습니다.

+0

위와 같은 방식으로'middle'을 쓸 수 있습니까? 업스트림에서 특정 응답을받을 때마다 같은 유형의 다른 'Proxy','foo'로 대체되므로 전체 효과는' up> ~> foo> ~> down? 'up> ~ foo'로 바뀔 수 있었습니까? 즉,이 예는 메시지 전달에 적합하지만 상태는 무엇입니까? –

+0

예. 내 답변을 어떻게 수행하는지에 대한 세부 정보로 업데이트했습니다. 간단히 말해'foo'를 직접 호출하면됩니다. –