At any time, when you want the pipeline to have two separate interfaces, you need to put the Proxy monad transformer inside yourself. That means you want a type:
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 ...
Given the following two clients for each interface:
client1 :: (Monad m, Proxy p) => () -> Client p Int Bool mr client2 :: (Monad m, Proxy p) => () -> Client p () Int mr
You would connect them to two server interfaces using:
oneInterface () = runProxy (twoInterfaces () >-> client1) main = runProxy (client1 >-> oneInterface)
To learn more about this trick, read the Branching, zips, and merges section of the current tutorial.
You can also do it the other way around. You can have Client with two separate interfaces and connect two different Server s. This can improve or improve your problem.
Note that this will be much simpler in pipes-4.0.0 (currently on Github), where types will be much more compressed and you won't need 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 mr client2 :: (Monad m) => () -> Client () Int mr
source share