diff --git a/pipes-misc.cabal b/pipes-misc.cabal
--- a/pipes-misc.cabal
+++ b/pipes-misc.cabal
@@ -1,5 +1,5 @@
 name:                pipes-misc
-version:             0.2.5.0
+version:             0.3.0.0
 synopsis:            Miscellaneous utilities for pipes, required by glazier-tutorial
 description:         Please see README.md
 homepage:            https://github.com/louispan/pipes-misc#readme
diff --git a/src/Pipes/Misc/Concurrent.hs b/src/Pipes/Misc/Concurrent.hs
--- a/src/Pipes/Misc/Concurrent.hs
+++ b/src/Pipes/Misc/Concurrent.hs
@@ -6,12 +6,14 @@
 import Control.Concurrent
 import Control.Concurrent.STM
 import Control.Monad
-import Control.Monad.Trans.Class
+import Control.Monad.Morph
 import Control.Monad.Trans.Except
 import Control.Monad.Trans.Maybe
+import Control.Monad.Trans.Reader
 import qualified Data.List.NonEmpty as NE
 import qualified Pipes as P
 import qualified Pipes.Concurrent as PC
+import qualified Pipes.Prelude as PP
 
 -- | Like Pipes.Concurrent.fromInput, but stays in STM.
 -- Using @hoist atomically@ to convert to IO monad seems to work.
@@ -77,3 +79,12 @@
           Nothing -> Left ys -- return successful reads so far
           Just x' -> Right $ x' NE.<| ys
 {-# INLINABLE batch #-}
+
+-- | Combine a 'Pipes.Concurrent.Input' and a 'ReaderT a STM r' into a 'Pipes.Producer' of the result r.
+-- That is, given a input of messages, and something that executes the messages to produce a result r,
+-- combine them to get a Producer of the executed results.
+execInput
+    :: (MonadTrans t, Monad (t STM))
+    => PC.Input a -> ReaderT a (t STM) b -> P.Producer' b (t STM) ()
+execInput input m = hoist lift (fromInputSTM input) P.>-> PP.mapM (runReaderT m)
+{-# INLINABLE execInput #-}
diff --git a/src/Pipes/Misc/State/Lazy.hs b/src/Pipes/Misc/State/Lazy.hs
--- a/src/Pipes/Misc/State/Lazy.hs
+++ b/src/Pipes/Misc/State/Lazy.hs
@@ -2,14 +2,10 @@
 
 module Pipes.Misc.State.Lazy where
 
-import Control.Concurrent.STM
 import Control.Lens
-import Control.Monad.Morph
 import Control.Monad.Reader
 import Control.Monad.State.Lazy
 import qualified Pipes as P
-import qualified Pipes.Concurrent as PC
-import qualified Pipes.Misc.Concurrent as PM
 import qualified Pipes.Prelude as PP
 
 -- | Store the output of the pipe into a MonadState.
@@ -43,21 +39,3 @@
     f s
     pure a
 {-# INLINABLE onState #-}
-
--- | Converts a 'Glazier.Gadget' into a 'Pipes.Pipe'
-rsPipe :: (Monad m, MonadTrans t, MonadState s (t m)) => ReaderT a (StateT s m) b -> P.Pipe a b (t m) r
-rsPipe m = forever $ do
-    a <- P.await
-    s <- get
-    -- This is the only line that is different between the Strict and Lazy version
-    ~(c, s') <- lift . lift $ runStateT (runReaderT m a) s
-    put s'
-    P.yield c
-{-# INLINABLE rsPipe #-}
-
--- | Convert a 'Pipes.Concurrent.Input' and a 'Glazier.Gadget' into a stateful 'Pipes.Producer' of commands to interpret.
-rsProducer ::
-  (MonadState s (t STM), MonadTrans t) =>
-  PC.Input a -> ReaderT a (StateT s STM) c -> P.Producer' c (t STM) ()
-rsProducer input m = hoist lift (PM.fromInputSTM input) P.>-> rsPipe m
-{-# INLINABLE rsProducer #-}
diff --git a/src/Pipes/Misc/State/Strict.hs b/src/Pipes/Misc/State/Strict.hs
--- a/src/Pipes/Misc/State/Strict.hs
+++ b/src/Pipes/Misc/State/Strict.hs
@@ -2,14 +2,9 @@
 
 module Pipes.Misc.State.Strict where
 
-import Control.Concurrent.STM
 import Control.Lens
-import Control.Monad.Morph
-import Control.Monad.Reader
 import Control.Monad.State.Strict
 import qualified Pipes as P
-import qualified Pipes.Concurrent as PC
-import qualified Pipes.Misc.Concurrent as PM
 import qualified Pipes.Prelude as PP
 
 -- | Store the output of the pipe into a MonadState.
@@ -43,21 +38,3 @@
     f s
     pure a
 {-# INLINABLE onState #-}
-
--- | Converts a 'Glazier.Gadget' into a 'Pipes.Pipe'
-rsPipe :: (Monad m, MonadTrans t, MonadState s (t m)) => ReaderT a (StateT s m) b -> P.Pipe a b (t m) r
-rsPipe m = forever $ do
-    a <- P.await
-    s <- get
-    -- This is the only line that is different between the Strict and Lazy version
-    (c, s') <- lift . lift $ runStateT (runReaderT m a) s
-    put s'
-    P.yield c
-{-# INLINABLE rsPipe #-}
-
--- | Convert a 'Pipes.Concurrent.Input' and a 'Glazier.Gadget' into a stateful 'Pipes.Producer' of commands to interpret.
-rsProducer ::
-  (MonadState s (t STM), MonadTrans t) =>
-  PC.Input a -> ReaderT a (StateT s STM) b -> P.Producer' b (t STM) ()
-rsProducer input m = hoist lift (PM.fromInputSTM input) P.>-> rsPipe m
-{-# INLINABLE rsProducer #-}
