首页 文章

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

提问于
浏览
6

说我有这个代码:

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

目前客户端始终发送 Int ,并接收 Bool . 但是,我希望客户端也能够查询到目前为止服务器已经看到的最高值 . 所以我还需要发送 () 和接收 Int 的通信 . 我可以将其编码为客户端发送 Either Int () ,并接收 Either Bool Int . 但是,我'd like to ensure that the two aren't混合 - 发送 Int 始终获得 Bool 响应 .

如何才能做到这一点?

2 回答

  • 5

    您可以做的是使用两个管道,根据您的两个用例量身定制 . 一台服务器将返回 Bool ,另一台服务器将返回 Int . 一个客户端会接受 Bool ,另一个客户端会接受 Int . 这两个客户实际上是 Pipe s . 一个会返回 Left Int ,另一个会返回 Right Bool (反之亦然) . 这两个服务器可以在 IORef 或类似的东西中传递 . 然后,您可以使用它来跟踪最大值 . 在两个客户端的末尾 Either Int Bool 可以用来对客户端返回的 LeftRight 情况做一些事情 .

  • 1

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

    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
        ...
    

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

    client1 :: (Monad m, Proxy p) => () -> Client p Int Bool m r
    client2 :: (Monad m, Proxy p) => () -> Client p ()  Int  m r
    

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

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

    要了解有关此技巧的更多信息,请阅读当前教程的Branching, zips, and merges部分 .

    你也可以反过来做 . 你可以拥有一个带有两个独立接口的 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
    

相关问题