diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Ben Gamari
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Ben Gamari nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Pipes/Interleave.hs b/Pipes/Interleave.hs
new file mode 100644
--- /dev/null
+++ b/Pipes/Interleave.hs
@@ -0,0 +1,120 @@
+module Pipes.Interleave ( interleave
+                        , combine
+                        , combineM
+                        , merge
+                        , mergeM
+                        , groupBy
+                        ) where
+                        
+import Control.Monad (liftM)
+import Data.List (sortBy)
+import Data.Function (on)
+import Data.Either (rights)
+import qualified Data.Sequence as Seq
+import Data.Foldable (toList)
+import Pipes
+
+-- $setup
+-- >>> import Pipes.Prelude
+-- >>> import Data.Function
+-- >>> import Data.Functor.Identity
+
+-- | Interleave elements from a set of 'Producers' such that the interleaved
+-- stream is increasing with respect to the given ordering.
+-- 
+-- >>> toList $ interleave compare [each [1,3..10], each [1,5..20]] 
+-- [1,1,3,5,5,7,9,9,13,17]
+-- 
+interleave :: (Monad m)
+           => (a -> a -> Ordering)   -- ^ ordering on elements
+           -> [Producer a m ()]      -- ^ element producers
+           -> Producer a m ()
+interleave compare producers = do
+    xs <- lift $ rights `liftM` mapM Pipes.next producers
+    go xs
+  where --go :: (Monad m, Functor m) => [(a, Producer a m ())] -> Producer a m ()
+        go [] = return ()
+        go xs = do let (a,producer):xs' = sortBy (compare `on` fst) xs
+                   yield a
+                   x' <- lift $ next producer
+                   go $ either (const xs') (:xs') x'
+{-# INLINABLE interleave #-}
+
+-- | Given a stream of increasing elements, combine those equal under the 
+-- given equality relation
+--
+-- >>> let append (k,v) (_,v') = return (k, v+v')
+-- >>> toList $ combine ((==) `on` fst) append (each [(1,1), (1,4), (2,3), (3,10)])
+-- [(1,5),(2,3),(3,10)]
+--
+combine :: (Monad m)
+        => (a -> a -> Bool)    -- ^ equality test
+        -> (a -> a -> a)       -- ^ combine operation
+        -> Producer a m r -> Producer a m r
+combine eq append = combineM eq (\a b->return $ append a b)
+{-# INLINEABLE combine #-}
+        
+-- | 'combine' with monadic side-effects in combine operation.
+combineM :: (Monad m)
+         => (a -> a -> Bool)    -- ^ equality test
+         -> (a -> a -> m a)     -- ^ combine operation
+         -> Producer a m r -> Producer a m r
+combineM eq append producer = lift (next producer) >>= either return (uncurry go)
+  where go a producer' = do
+          n <- lift $ next producer'
+          case n of
+            Left r                 -> yield a >> return r
+            Right (a', producer'')
+              | a `eq` a'          -> do a'' <- lift $ append a a'
+                                         go a'' producer''
+              | otherwise          -> yield a >> go a' producer''
+{-# INLINABLE combineM #-}
+   
+-- | Equivalent to 'combine' composed with 'interleave'
+--
+-- >>> let append (k,v) (_,v') = return (k, v+v')
+-- >>> let producers = [ each [(i,2) | i <- [1,3..10]], each [(i,10) | i <- [1,5..20]] ] :: [Producer (Int,Int) Identity ()]
+-- >>> toList $ merge (compare `on` fst) append producers
+-- [(1,12),(3,2),(5,12),(7,2),(9,12),(13,10),(17,10)]
+-- 
+merge :: (Monad m)
+      => (a -> a -> Ordering)    -- ^ ordering on elements
+      -> (a -> a -> a)           -- ^ combine operation
+      -> [Producer a m ()]       -- ^ producers of elements
+      -> Producer a m ()
+merge compare append = mergeM compare (\a b->return $ append a b)
+{-# INLINABLE merge #-}
+
+-- | Merge with monadic side-effects in combine operation.
+mergeM :: (Monad m)
+       => (a -> a -> Ordering)    -- ^ ordering on elements
+       -> (a -> a -> m a)         -- ^ combine operation
+       -> [Producer a m ()]       -- ^ producers of elements
+       -> Producer a m ()
+mergeM compare append =
+    combineM (\a b->compare a b == EQ) append . interleave compare
+{-# INLINABLE mergeM #-}
+
+-- | Split stream into groups of equal elements.
+-- Note that this is a non-local operation: if the 'Producer' generates
+-- a large run of equal elements, all of them will remain in memory until the
+-- run ends.
+--
+-- >>> toList $ groupBy ((==) `on` fst) (each [(1,1), (1,4), (2,3), (3,10)])
+-- [[(1,1),(1,4)],[(2,3)],[(3,10)]]
+-- 
+groupBy :: (Monad m)
+        => (a -> a -> Bool)    -- ^ equality test
+        -> Producer a m r -> Producer [a] m r
+groupBy eq producer =
+    lift (next producer) >>= either return (\(x,producer)->go (Seq.singleton x) producer)
+  where -- go :: Monad m => Seq.Seq a -> Producer a m r -> Producer [a] m r
+        go xs producer' = do
+          n <- lift $ next producer'
+          case n of
+            Left r                 -> yield (toList xs) >> return r
+            Right (x, producer'')
+              | x `eq` x0     -> go (xs Seq.|> x) producer''
+              | otherwise     -> yield (toList xs) >> go (Seq.singleton x) producer''
+              where x0 Seq.:< _ = Seq.viewl xs
+{-# INLINABLE groupBy #-}
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/pipes-interleave.cabal b/pipes-interleave.cabal
new file mode 100644
--- /dev/null
+++ b/pipes-interleave.cabal
@@ -0,0 +1,20 @@
+name:                pipes-interleave
+version:             0.1
+synopsis:            Interleave and merge streams of elements
+description:         Interleave and merge streams of elements
+homepage:            http://github.com/bgamari/pipes-interleave
+license:             BSD3
+license-file:        LICENSE
+author:              Ben Gamari
+maintainer:          bgamari.foss@gmail.com
+-- copyright:           
+category:            Data
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Pipes.Interleave
+  build-depends:       base >=4.6 && <4.7,
+                       containers >=0.5 && < 0.6,
+                       pipes >=4.0 && <4.1
+  default-language:    Haskell2010
