Q:

Is a one-yield-per-await restricted pipe possible?

I'm working with pipes-4.0.0. In that library, the number of yields to downstream a pipe makes is in general unrelated to the number of awaits from upstream.
But suppose I wanted to build a restricted pipe that enforced that one and only one yield is performed for each await, while still being able to sequence these kinds of pipes using monadic (>>=).
I have observed that, in the bidirectional case, each value requested from upstream by a Proxy is matched with a value sent back. So maybe what I'm searching for is a function of type Proxy a' a () b m r -> Pipe a (Either b a') m r that "reflects" the values going upstream, turning them into additional yields to downstream. Or, less generally, Client a' a -> Pipe a a'. Is such a function possible?

A:

You definitely do not want to use pipes for this.  But, what you can do is define a restricted type that does this, do all your connections and logic within that restricted type, then promote it to a Pipe when you are done.
The type in question that you want is this, which is similar to the netwire Wire:
{-# LANGUAGE DeriveFunctor #-}

import Control.Monad.Trans.Free  -- from the 'free' package

data WireF a b x = Pass (a -> (b, x)) deriving (Functor)

type Wire a b = FreeT (WireF a b)

That's automatically a monad and a monad transformer since it is implemented in terms of FreeT.  Then you can implement this convenient operation:
pass :: (Monad m) => (a -> b) -> Wire a b m ()
pass f = liftF $ Pass (\a -> (f a, ()))

... and assemble custom wires using monadic syntax:
example :: Wire Int Int IO ()
example = do
    pass (+ 1)
    lift $ putStrLn "Hi!"
    pass (* 2)

Then when you're done connecting things with this restricted Wire type you can promote it to a Pipe:
promote :: (Monad m) => Wire a b m r -> Pipe a b m r
promote w = do
    x <- lift $ runFreeT w
    case x of
        Pure r -> return r
        Free (Pass f) -> do
            a <- await
            let (b, w') = f a
            yield b
            promote w'

Note that you can define an identity and wire and wire composition:
idWire :: (Monad m) => Wire a a m r
idWire = forever $ pass id

(>+>) :: (Monad m) => Wire a b m r -> Wire b c m r -> Wire a c m r
w1 >+> w2 = FreeT $ do
    x <- runFreeT w2
    case x of
        Pure       r   -> return (Pure r)
        Free (Pass f2) -> do
            y <- runFreeT w1
            case y of
                Pure       r   -> return (Pure r)
                Free (Pass f1) -> return $ Free $ Pass $ \a ->
                        let (b, w1') = f1 a
                            (c, w2') = f2 b
                        in  (c, w1' >+> w2')

I'm pretty sure those form a Category:
idWire >+> w = w

w >+> idWire = w

(w1 >+> w2) >+> w3 = w1 >+> (w2 >+> w3)

Also, I'm pretty sure that promote obeys the following functor laws:
promote idWire = cat

promote (w1 >+> w2) = promote w1 >-> promote w2

