说我有这个代码:
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 回答
您可以做的是使用两个管道,根据您的两个用例量身定制 . 一台服务器将返回
Bool
,另一台服务器将返回Int
. 一个客户端会接受Bool
,另一个客户端会接受Int
. 这两个客户实际上是Pipe
s . 一个会返回Left Int
,另一个会返回Right Bool
(反之亦然) . 这两个服务器可以在IORef
或类似的东西中传递 . 然后,您可以使用它来跟踪最大值 . 在两个客户端的末尾Either Int Bool
可以用来对客户端返回的Left
和Right
情况做一些事情 .每当您希望管道具有两个单独的接口时,您必须将
Proxy
monad变换器嵌套在其自身中 . 这意味着你想要的类型:给出每个接口的以下两个客户端:
您将使用以下命令将它们连接到两个服务器接口:
要了解有关此技巧的更多信息,请阅读当前教程的Branching, zips, and merges部分 .
你也可以反过来做 . 你可以拥有一个带有两个独立接口的
Client
并连接两个不同的Server
. 这可能会或可能不会更好地适合您的问题 .请注意,这将在
pipes-4.0.0
(目前在Github上)变得更加简单,其中类型将更加简洁,您将不需要runIdentityP
: