如何拥有多种通信类型的管道?

ajp*_*ajp 6 haskell haskell-pipes

说我有这个代码:

import Control.Monad.State hiding (StateT)
import Control.Proxy

server :: (Proxy p, Monad m) => Int -> Server p Int Bool (StateT Int m) ()
server = runIdentityK loop
    where loop arg = do
        currMax <- lift get
        lift $ put $ max currMax arg
        nextArg <- respond (even arg)
        loop nextArg

client :: (Proxy p, Monad m) => Client p Int Bool m ()
client = runIdentityP loop
    where loop = go 1
          go i = do
            isEven <- request i
            go $ if isEven
                then i `div` 2
                else i * 3 + 1
Run Code Online (Sandbox Code Playgroud)

目前,客户端始终发送Int和接收Bool.但是,我希望客户端也能够查询到目前为止服务器已经看到的最高值.所以我也需要发送()和接收的通信Int.我可以将其编码为客户端发送Either Int ()和接收Either Bool Int.但是,我想确保两者没有混合 - 发送Int总是得到Bool响应.

如何才能做到这一点?

Gab*_*lez 5

每当您希望管道具有两个单独的接口时,您必须将Proxymonad变换器嵌套在其自身中.这意味着你想要的类型:

twoInterfaces
    :: (Monad m, Proxy p1, Proxy p2 )
    => () -> Int -> Server p1 Int Bool (Server p2 () Int m) r
twoInterfaces () n = runIdentityP . hoist runIdentityP $ do
    x <- respond A        -- Use outer interface
    y <- lift $ respond B -- Use inner interface
    ...
Run Code Online (Sandbox Code Playgroud)

给出每个接口的以下两个客户端:

client1 :: (Monad m, Proxy p) => () -> Client p Int Bool m r
client2 :: (Monad m, Proxy p) => () -> Client p ()  Int  m r
Run Code Online (Sandbox Code Playgroud)

您将使用以下命令将它们连接到两个服务器接口:

oneInterface () = runProxy (twoInterfaces () >-> client1)

main = runProxy (client1 >-> oneInterface)
Run Code Online (Sandbox Code Playgroud)

要了解有关此技巧的更多信息,请阅读当前教程的分支,压缩和合并部分.

你也可以反过来做.你可以拥有一个Client带有两个独立接口并连接两个不同Server的接口.这可能会或可能不会更好地适合您的问题.

请注意,这将变得更加简单pipes-4.0.0(目前在Github上),其中类型将更加简洁,您将不需要runIdentityP:

twoInterfaces
    :: (Monad m) => () -> Int -> Server Int Bool (Server () Int m) r
twoInterface () n = do
    x <- respond A
    y <- lift $ respond B
   ...

client1 :: (Monad m) => () -> Client Int Bool m r
client2 :: (Monad m) => () -> Client ()  Int  m r
Run Code Online (Sandbox Code Playgroud)