6

Say I have this code:

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

Currently the client always sends Int, and receives Bool. However, I want the client to also be able to query for the highest value that the server has seen so far. So I also need communication of sending () and receiving Int. I could encode this as the client sending Either Int (), and receiving Either Bool Int. However, I'd like to ensure that the two aren't mixed - sending an Int always gets a Bool response.

How can this be done?

4

2 回答 2

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

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

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)

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

你也可以反过来做。您可以拥有一个Client带有两个独立接口的 a 并连接两个不同Server的 s。这可能更适合您的问题,也可能不适合。

请注意,这将在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
于 2013-06-20T15:59:41.847 回答
1

您可以做的是使用两个管道,为您的两个用例量身定制。一个服务器将返回一个Bool,另一个将返回一个Int。一个客户会接受一个Bool,另一个会接受一个Int。这两个客户实际上是Pipes。一个会返回 a Left Int,另一个会返回 a Right Bool(反之亦然)。两台服务器可以以IORef某种方式传递。然后,您可以使用它来跟踪最大值。两个客户端结束时的Pipe取值Either Int Bool可用于对客户端返回的LeftRight案例做一些事情。

于 2013-06-20T11:40:15.447 回答