도관

2014-12-28 2 views
0
내가 도관 간단한 네트워크 프로토콜을 구현하고

로 스트리밍 네트워크 프로토콜에서 결과를 얻기; 프로토콜은 각 메시지 앞에 메시지 길이를 설명하는 uint32 접두사가있는 메시지 스트림입니다. (메시지 데이터에는 내부 구조가 더 있습니다. 그러나 예상되는 메시지 크기가 작기 때문에 전체 메시지를 파싱하기 전에 메모리로 읽는 것이 좋기 때문에 여기서는 중요하지 않습니다.) 프로토콜은 클라이언트에게 서버에 요청을 포함하는 메시지를 보내고 응답을 포함하는 메시지를 반환하는 서버 (작업의 동시성없이)와 양방향에서 동일합니다. 지금까지도관

import qualified Data.ByteString as B 
import qualified Data.ByteString.Lazy as LB 

data Message = ... 
parseMessage :: LB.ByteString -> Message 
serializeMessage :: Message -> LB.ByteString 

messageReceiver :: Conduit B.ByteString IO Message 
messageReceiver = loop 
    where 
    loop = do 
     lenBytes <- takeCE 4 =$= sinkLazy 
     message <- takeCE (runGet getWord32be' lenBytes) =$= sinkLazy 
     yield $ parseMessage message 
     loop 

messageSender :: Conduit Message IO B.ByteString 
messageSender = concatMapC $ \message -> 
    let messageBytes = serializeMessage message 
     lenBytes = runPut $ putWord32be' (LB.length messageBytes) 
    in map LB.toStrict [lenBytes, messageBytes] 

, 그래서 좋은 :

내 생각은 ByteString 및 그 반대로 (여러 가능한 메시지를 설명하는 내 자신의 형) Message에서 이동하는 두 가지 간단한 도관의 상단에있는 코드를 구축하는 것이 었습니다 ; 나는 확신하지만, 또는 적어도, 코드 typechecks는 그것을 작성하는 더 우아한 방법 (messageReceiver 특히 루프)이있다. 이제 서버에 연결하고 요청을 보내고 응답을 받고 연결을 끊으려는 내용을 작성하려고합니다.

runOneCommand request = do 
    yield request 
    response <- await 
    return response 

그러나, 나는 실제로 네트워크 클라이언트 소스에이를 연결하고 나는 다시 "응답"값을 얻을 수있는 방법에 싱크하는 방법을 잘 모르겠어요 :이 썼다. 실패

appSource agent $$ messageReceiver =$= runOneCommand =$= messageSender =$= appSink agent 

컴파일 :

Couldn't match type `Data.Maybe.Maybe SSH.Agent.Message' with `()' 
Expected type: conduit-1.2.3.1:Data.Conduit.Internal.Conduit.Conduit 
       SSH.Agent.Message ghc-prim:GHC.Types.IO SSH.Agent.Message 
    Actual type: conduit-1.2.3.1:Data.Conduit.Internal.Conduit.ConduitM 
       SSH.Agent.Message 
       SSH.Agent.Message 
       ghc-prim:GHC.Types.IO 
       (Data.Maybe.Maybe SSH.Agent.Message) 
In the return type of a call of `Main.runOneCommand' 
In the first argument of `(conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$=)', namely 
    `Main.runOneCommand SSH.Agent.RequestIdentities' 
In the second argument of `(conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$=)', namely 
    `Main.runOneCommand SSH.Agent.RequestIdentities 
    conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$= 
    Main.messageSender 
    conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$= 
     Data.Conduit.Network.appSink agent' 
Couldn't match type `Data.Maybe.Maybe SSH.Agent.Message' with `()' 
Expected type: conduit-1.2.3.1:Data.Conduit.Internal.Conduit.Conduit 
       SSH.Agent.Message ghc-prim:GHC.Types.IO SSH.Agent.Message 
    Actual type: conduit-1.2.3.1:Data.Conduit.Internal.Conduit.ConduitM 
       SSH.Agent.Message 
       SSH.Agent.Message 
       ghc-prim:GHC.Types.IO 
       (Data.Maybe.Maybe SSH.Agent.Message) 
In the return type of a call of `Main.runOneCommand' 
In the first argument of `(conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$=)', namely 
    `Main.runOneCommand SSH.Agent.RequestIdentities' 
In the second argument of `(conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$=)', namely 
    `Main.runOneCommand SSH.Agent.RequestIdentities 
    conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$= 
    Main.messageSender 
    conduit-1.2.3.1:Data.Conduit.Internal.Conduit.=$= 
     Data.Conduit.Network.appSink agent' 

네트워크 클라이언트의 싱크 ()의 반환 형식이 아닌 Message 기대 때문에,이 실패 내가 제대로 여기 유형을 다음있어 가정이 시도 그래서 여기에 다른 형태의 도관 구성이 필요 하겠지만, 나는 무엇을 모르겠습니다. 도관 이후

response <- appSource agent $$ messageReceiver =$= runOneCommand RequestIdentities `fuseUpstream` messageSender `fuseUpstream` appSink agent 

그것은 그 대답은 더 이상 적용에 fuseUpstream 유형의 scariness에 대한 경고를 보인다 (:

답변

0

이 질문을 게시 후, 나는 fuseUpstream의 방향으로 날을 지적 https://stackoverflow.com/a/23925496/31490 발견 유형은 단순했다?); 비교 :

(=$=) :: Monad m => Conduit a m b -> ConduitM b c m r -> ConduitM a c m r 
fuseBoth :: Monad m => ConduitM a b m r1 -> ConduitM b c m r2 -> ConduitM a c m (r1, r2) 
fuseUpstream :: Monad m => ConduitM a b m r -> Conduit b m c -> ConduitM a c m r 
관련 문제