我正在尝试使用管道来模拟双向Proxy
实例是理想的问题。基本上,我有类似以下架构的东西:
api logic
| ^
| |
v |
A A'
layer 1
B B'
| ^
| |
v |
layer 2
基本上,我有layer 1
这是一个双向变换器。该模型是基于拉式的,因此我可以通过logic
组件的拉动来触发消息流转换。
所以我应该layer1 :: Proxy A' A B' B m x
,我的想法是layer1
从A
提取api
,然后某些转化A -> B
会使用B'
从layer2开始,应用B' -> A'
并将其传递给logic
。
不清楚的是:我知道如何request
A
并回复B
,但如何从A'
生成B'
?库中似乎没有任何组合器适合这里......
答案 0 :(得分:2)
您需要注意三种类型:Client
可以request
但respond
Server
,respond
可以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
并打印出回复。 transform
将Token
转换为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