p1 `idealOp` p2
What's the type signature you're shooting for with `idealOp`?
The idea of what you're doing looks sane to me. To put a few names to it:
dup :: a ~> (a,a)
dup = arr $ \a -> (a,a)
decouple :: (a,b) ~> Either a b
decouple = forever $ await >>= \(a,b) -> yield (Left a) >> yield (Right b)
(&&&) :: (a ~> b) -> (a ~> c) -> (a ~> (b,c))
idealOp :: (a ~> b) -> (a ~> b) -> (a ~> b)
idealOp p1 p2 = dup >>> decouple >>> (p1 +++ p2)
It seems that for this case, decouple might be important. While they're running in lock step, p1 might yield but p2 might not. If you *want* their yields to be coupled, then instead try this:
merge :: (a,a) ~> a
merge = forever $ await >>= \(a,a') -> yield a >> yield a'
idealOp :: (a ~> b) -> (a ~> b) -> (a ~> b)
idealOp p1 p2 = (p1 &&& p2) >>> merge
I'm writing in pseudo-code, but I hope what I'm saying is clear.