如何正确使用双向管道?

How to use bidirectional pipes properly?

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

api   logic
  |   ^
  |   | 
  v   | 
  A   A'
 layer 1
  B   B' 
  |   ^
  |   |
  v   |
 layer 2     

所以基本上,我有 layer 1 这是一个双向变压器。该模型是基于拉动的,因此我将消息流转换由 logic 组件的拉动触发。

所以我应该 layer1 :: Proxy A' A B' B m x,想法是 layer1api 中提取 A,做一些转换 A -> B 然后使用 B' 来自 layer2,应用 B' -> A' 并将其传递给 logic

不清楚的是:我知道如何 request 一个 A 并响应一个 B 但我如何从 [=19 生成 A' =]?库中似乎没有适合这里的组合器...

您需要注意三种类型:Clients 可以 request 但永远不会 respondServers 可以 respond 但永远不会requestProxy 都可以。

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

令人困惑的一点:因为管道本身是同步的,所以您需要获得一个初始值来传递并启动。要么将其传递给 request(制作一个由 (>~>) 组成的拉式管道),要么将其传递给 respond(制作一个由 (>+>) 组合的推式管道) .然后将初始值传递到组合管道中,为您提供可以转到 runEffect.

Effect m r

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

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

client 生成请求 Tokens 并打印出响应。 transformToken 转换为 Yoken(嘿,键彼此紧挨着)并将它们向上游传递。它还通过大写和附加 ! 将响应变成喊叫。 server 接收 Yokens 并生成请求的 yos 数量。

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