2013-06-20 36 views
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。但是,我想确保两者不混合 - 发送Int总会得到Bool响应。

这怎么办?

+0

为什么会也不行?如果您将任一个发送到服务器,您可以在左或右情况下进行模式匹配,并发送适当的左或右返回。 – Dwilson

+3

@Dwilson:他想静态确保发送'Int'总是返回一个'Bool'。使用'Either'会将此检查移至运行时。 –

回答

5

任何时候你想要一个流水线有两个独立的接口,你必须在其本身嵌套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 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 
+0

由于(根据我的理解),我不得不通过将内部'Server()Int m'扩展到'Proxy xy()Int m'来稍微修改第二个(管道4.0.0)片段来编译。有一个部分应用类型与GHC – ajp

+1

@ajp的同义词您也可以使用'服务器'来让它工作。每个多态类型的同义词都有一个具体的,不需要完全适用于所有类型参数。 –

1

你可以做的是使用两条管道,为你的两个用例量身定制。一台服务器将返回一个Bool,另一台将返回一个Int。一个客户会接受Bool,另一个客户会接受Int。这两个客户实际上是Pipe s。一个会返回一个Left Int,另一个会返回一个Right Bool(反之亦然)。这两台服务器可以通过IORef或类似的东西传递。然后,您可以使用它来跟踪最大值。 A PipeEither Int Bool在两个客户端的末尾可用于对客户返回的LeftRight个案进行处理。