diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+0.7.2.0
+-------
+- contraproduce, contraencoded, Splitter, splitIntoLines, nest    
+
 0.7.1.0
 -------
 - contramapFoldable, contramapEnumerable.
diff --git a/process-streaming.cabal b/process-streaming.cabal
--- a/process-streaming.cabal
+++ b/process-streaming.cabal
@@ -1,5 +1,5 @@
 name:          process-streaming
-version:       0.7.1.0
+version:       0.7.2.0
 license:       BSD3
 license-file:  LICENSE
 data-files:    
diff --git a/src/System/Process/Streaming.hs b/src/System/Process/Streaming.hs
--- a/src/System/Process/Streaming.hs
+++ b/src/System/Process/Streaming.hs
@@ -70,12 +70,21 @@
         , fromFoldlM
         , intoLazyBytes
         , intoLazyText 
+        , intoList
         , unwanted
         , DecodingFunction
         , encoded
         , SiphonOp (..)
         , contramapFoldable
         , contramapEnumerable
+        , contraproduce
+        , contraencoded
+        , Splitter 
+        , splitter
+        , splitIntoLines
+        , tweakSplits
+        , rejoin 
+        , nest
         -- * Handling lines
         , Lines
         , toLines
@@ -119,6 +128,7 @@
 import Control.Applicative.Lift
 import Control.Monad
 import Control.Monad.Trans.Free hiding (Pure)
+import qualified Control.Monad.Trans.Free as FREE
 import Control.Monad.Trans.Except
 import qualified Control.Foldl as L
 import Control.Exception
@@ -361,6 +371,9 @@
 intoLazyText :: Siphon Text e TL.Text
 intoLazyText = fromFoldl (fmap TL.fromChunks L.list)
 
+intoList :: Siphon b e [b]
+intoList = fromFoldl L.list
+
 {-| 
    Builds a 'Siphon' out of a computation that does something with
    a 'Producer', but may fail with an error of type @e@.
@@ -566,6 +579,73 @@
 
 
 {-|
+    Like encoded, but works on 'SiphonOp's. 
+ -}
+contraencoded :: DecodingFunction bytes text
+        -- ^ A decoding function.
+        -> Siphon bytes e (a -> b)
+        -- ^ A 'Siphon' that determines how to handle decoding leftovers.
+        -- Pass @pure id@ to ignore leftovers. Pass @unwanted id@ to abort
+        -- the computation with an explicit error if leftovers remain. Pass
+        -- '_leftoverX' to throw a 'LeftoverException' if leftovers remain.
+        -> SiphonOp e a text
+        -> SiphonOp e b bytes 
+contraencoded decoder leftovers (SiphonOp siph) = SiphonOp $ 
+    encoded decoder leftovers siph
+
+
+{-|
+    Build a 'Splitter' out of a function that splits a 'Producer' while
+    preserving streaming.
+
+    See the section /FreeT Transformations/ in the documentation for the
+    /pipes-text/ package, and also the documentation for the /pipes-group/
+    package.
+-}
+splitter :: (forall r. Producer b IO r -> FreeT (Producer b IO) IO r) -> Splitter b
+splitter = Splitter
+
+{-|
+    Specifies a transformation that will be applied to each individual
+    split, represented as a 'Producer'.
+-}
+tweakSplits :: (forall r. Producer b IO r -> Producer b IO r) -> Splitter b -> Splitter b
+tweakSplits f (Splitter s) = Splitter $ fmap (transFreeT f) s
+
+
+{-|
+    Flattens the 'Splitter', returning a function from 'Producer' to
+    'Producer' which can be passed to functions like 'contraproduce'.
+-}
+rejoin :: forall b r. Splitter b -> Producer b IO r -> Producer b IO r
+rejoin (Splitter f) = go . f 
+  where
+    -- code copied from the "concats" function from the pipes-group package
+    go f = do
+        x <- lift (runFreeT f)
+        case x of
+            FREE.Pure r -> return r
+            Free p -> do
+                f' <- p
+                go f'
+
+
+splitIntoLines :: Splitter T.Text 
+splitIntoLines = splitter $ getConst . T.lines Const
+
+
+{-|
+    Process each individual split created by a 'Splitter' using a 'Siphon'.
+ -}
+nest :: Splitter b -> Siphon b Void a -> SiphonOp e r a -> SiphonOp e r b
+nest (Splitter sp) nested = 
+    contraproduce $ \producer -> iterT runRow (hoistFreeT lift $ sp producer)
+  where
+    runRow p = do
+        (r, innerprod) <- lift $ fmap (either absurd id) (runSiphon nested p)
+        P.yield r >> innerprod
+
+{-|
     A newtype wrapper with functions for working on the inputs of
     a 'Siphon', instead of the outputs. 
  -}
@@ -638,6 +718,9 @@
 contramapEnumerable :: Enumerable t => (a -> t IO b) -> SiphonOp e r b -> SiphonOp e r a
 contramapEnumerable unwinder (getSiphonOp -> s) = SiphonOp $
     siphon' $ runSiphon s . flip for (enumerate . toListT . unwinder) 
+
+contraproduce :: (forall r. Producer a IO r -> Producer b IO r) -> SiphonOp e r b -> SiphonOp e r a
+contraproduce f (getSiphonOp -> s) = SiphonOp $ siphon' $ runSiphon s . f
 
 {-|
     Specifies a transformation that will be applied to each line of text,
diff --git a/src/System/Process/Streaming/Internal.hs b/src/System/Process/Streaming/Internal.hs
--- a/src/System/Process/Streaming/Internal.hs
+++ b/src/System/Process/Streaming/Internal.hs
@@ -16,6 +16,7 @@
         Siphon_(..),
         exhaustive,
         Lines(..),
+        Splitter(..),
         combined,
         manyCombined,
         Stage(..)
@@ -383,6 +384,8 @@
 instance Functor Lines where
   fmap f (Lines func lt) = Lines (\x y z -> fmap (bimap f id) $ func x y z) lt
 
+
+newtype Splitter b = Splitter { getSplitter :: forall r. Producer b IO r -> FreeT (Producer b IO) IO r } 
 
 
 {-|
diff --git a/src/System/Process/Streaming/Tutorial.hs b/src/System/Process/Streaming/Tutorial.hs
--- a/src/System/Process/Streaming/Tutorial.hs
+++ b/src/System/Process/Streaming/Tutorial.hs
@@ -65,6 +65,9 @@
 
     -- * Early termination
     -- $earlytermination
+
+    -- * Collecting @stdout@ as lines of Text
+    -- $collstdouttextlines
     ) where
 
 import System.Process.Streaming
@@ -214,3 +217,22 @@
 >>> executeFallibly (pipeo (siphon (\_ -> return (Left "oops")))) (shell "sleep infinity")
 Left "oops"
 -}
+
+
+{- $collstdouttextlines  
+
+
+>>> :{ 
+   let 
+      osiphon
+          = getSiphonOp 
+          $ contraencoded decodeUtf8 (pure id) 
+          $ nest splitIntoLines intoLazyText 
+          $ SiphonOp intoList
+   in 
+   execute (pipeo osiphon) (shell "{ echo aa ; echo bb ; }")
+   :}
+(ExitSuccess,["aa","bb"])
+-}
+
+
