如何正确使用双向管道?

时间:2017-06-08 20:24:48

标签: haskell-pipes

我正在尝试使用管道来模拟双向Proxy实例是理想的问题。基本上,我有类似以下架构的东西:

api   logic
  |   ^
  |   | 
  v   | 
  A   A'
 layer 1
  B   B' 
  |   ^
  |   |
  v   |
 layer 2     
基本上,我有layer 1这是一个双向变换器。该模型是基于拉式的,因此我可以通过logic组件的拉动来触发消息流转换。

所以我应该layer1 :: Proxy A' A B' B m x,我的想法是layer1A提取api,然后某些转化A -> B会使用B'从layer2开始,应用B' -> A'并将其传递给logic

不清楚的是:我知道如何request A并回复B,但如何从A'生成B' ?库中似乎没有任何组合器适合这里......

1 个答案:

答案 0 :(得分:2)

您需要注意三种类型:Client可以requestrespond Serverrespond可以request但永远不会Proxy 1}}和request可以同时执行这两项操作。

respond / request的参数是要发送的值,绑定的结果分别是响应/请求。这对于respond(你绑定响应)有直观的意义,但在我点击Control.Monad.forever之前花了一点时间(你绑定了 next 请求) 。它使您的处理步骤整洁有点递归功能。 (我最初的本能是使用request,它适用于单向管道,但在这里是错误的工具。)

令人困惑的一点:因为管道本身是同步的,所以你需要获得一个初始值来传递并启动它。要么将其传递给(>~>)(制作一个由respond组成的拉动管道),要么将其传递给(>+>)(制作一个由Effect m r组成的推送管道) 。然后将初始值传递到撰写的管道中,为您提供可以转到runEffect的{​​{1}}。

我在下面的示例中使用了拉取管道,因为它适合您的API请求隐喻。它实现了这个三阶段双向管道:

+--------+  Yoken +-----------+  Token +--------+
|        |<-------|           |<-------|        |
| server |        | transform |        | client |
|        |------->|           |------->|        |
+--------+ String +-----------+ String +--------+
                               (shouty)

client生成请求Token并打印出回复。 transformToken转换为Yoken s(嘿,密钥紧挨着彼此)并将它们传递到上游。它还通过上传和附加!将响应变为喊叫。 server收到Yoken个并生成请求的yo s。

import Data.Char
import Control.Monad
import Control.Monad.IO.Class
import Pipes.Core
import System.IO

data Token = Token Int
data Yoken = Yoken Int

main :: IO ()
main = do
    hSetBuffering stdout NoBuffering
    -- We have to get an initial n outside the pipeline to kick things off.
    putStr "N? "
    n <- read <$> getLine
    runEffect $ server >+> transform >+> client $ Token n

-- The "server" generates a string of "yo"s based on the number inside the Yoken
server :: Monad m => Yoken -> Server Yoken String m a
server (Yoken n) = (respond . concat $ replicate n "yo") >>= server

-- A processing step just for the sake of having one, turn the Token into a 
-- Yoken, upcase the string, and append a "!".
transform :: Monad m => Token -> Proxy Yoken String Token String m a
transform (Token t) = do
    resp <- request $ Yoken t
    next <- respond $ map toUpper resp ++ "!"
    transform next

-- Clients request "yo"s, by sending `Token`s upstream.
client :: Token -> Client Token String IO a
client t = do
    resp <- request t
    n <- liftIO $ putStrLn resp *> putStr "N? " *> fmap read getLine
    client $ Token n