diff --git a/Data/Vector.hs b/Data/Vector.hs
--- a/Data/Vector.hs
+++ b/Data/Vector.hs
@@ -13,11 +13,57 @@
 --
 
 module Data.Vector (
-  Vector(..), module Data.Vector.IVector
+  Vector,
+
+  -- * Length information
+  length, null,
+
+  -- * Construction
+  empty, singleton, cons, snoc, replicate, (++), copy,
+
+  -- * Accessing individual elements
+  (!), head, last, indexM, headM, lastM,
+
+  -- * Subvectors
+  slice, init, tail, take, drop,
+
+  -- * Permutations
+  accum, (//), update, backpermute, reverse,
+
+  -- * Mapping
+  map, concatMap,
+
+  -- * Zipping and unzipping
+  zipWith, zipWith3, zip, zip3, unzip, unzip3,
+
+  -- * Filtering
+  filter, takeWhile, dropWhile,
+
+  -- * Searching
+  elem, notElem, find, findIndex,
+
+  -- * Folding
+  foldl, foldl1, foldl', foldl1', foldr, foldr1,
+
+  -- * Specialised folds
+  and, or, sum, product, maximum, minimum,
+
+  -- * Unfolding
+  unfoldr,
+
+  -- * Scans
+  prescanl, prescanl',
+
+  -- * Enumeration
+  enumFromTo, enumFromThenTo,
+
+  -- * Conversion to/from lists
+  toList, fromList
 ) where
 
-import           Data.Vector.IVector
-import qualified Data.Vector.Mutable as Mut
+import           Data.Vector.IVector ( IVector(..) )
+import qualified Data.Vector.IVector    as IV
+import qualified Data.Vector.Mutable.ST as Mut
 
 import Control.Monad.ST ( runST )
 
@@ -25,10 +71,27 @@
 import GHC.Prim ( Array#, unsafeFreezeArray#, indexArray#, (+#) )
 import GHC.Base ( Int(..) )
 
+import Prelude hiding ( length, null,
+                        replicate, (++),
+                        head, last,
+                        init, tail, take, drop, reverse,
+                        map, concatMap,
+                        zipWith, zipWith3, zip, zip3, unzip, unzip3,
+                        filter, takeWhile, dropWhile,
+                        elem, notElem,
+                        foldl, foldl1, foldr, foldr1,
+                        and, or, sum, product, minimum, maximum,
+                        enumFromTo, enumFromThenTo )
+
+import qualified Prelude
+
 data Vector a = Vector {-# UNPACK #-} !Int
                        {-# UNPACK #-} !Int
                                       (Array# a)
 
+instance Show a => Show (Vector a) where
+    show = (Prelude.++ " :: Data.Vector.Vector") . ("fromList " Prelude.++) . show . toList
+
 instance IVector Vector a where
   {-# INLINE vnew #-}
   vnew init = runST (do
@@ -42,15 +105,341 @@
   {-# INLINE unsafeSlice #-}
   unsafeSlice (Vector i _ arr#) j n = Vector (i+j) n arr#
 
-  {-# INLINE unsafeIndex #-}
-  unsafeIndex (Vector (I# i#) _ arr#) (I# j#) f
-    = case indexArray# arr# (i# +# j#) of (# x #) -> f x
+  {-# INLINE unsafeIndexM #-}
+  unsafeIndexM (Vector (I# i#) _ arr#) (I# j#)
+    = case indexArray# arr# (i# +# j#) of (# x #) -> return x
 
 instance Eq a => Eq (Vector a) where
   {-# INLINE (==) #-}
-  (==) = eq
+  (==) = IV.eq
 
 instance Ord a => Ord (Vector a) where
   {-# INLINE compare #-}
-  compare = cmp
+  compare = IV.cmp
+
+-- Length
+-- ------
+
+length :: Vector a -> Int
+{-# INLINE length #-}
+length = IV.length
+
+null :: Vector a -> Bool
+{-# INLINE null #-}
+null = IV.null
+
+-- Construction
+-- ------------
+
+-- | Empty vector
+empty :: Vector a
+{-# INLINE empty #-}
+empty = IV.empty
+
+-- | Vector with exaclty one element
+singleton :: a -> Vector a
+{-# INLINE singleton #-}
+singleton = IV.singleton
+
+-- | Vector of the given length with the given value in each position
+replicate :: Int -> a -> Vector a
+{-# INLINE replicate #-}
+replicate = IV.replicate
+
+-- | Prepend an element
+cons :: a -> Vector a -> Vector a
+{-# INLINE cons #-}
+cons = IV.cons
+
+-- | Append an element
+snoc :: Vector a -> a -> Vector a
+{-# INLINE snoc #-}
+snoc = IV.snoc
+
+infixr 5 ++
+-- | Concatenate two vectors
+(++) :: Vector a -> Vector a -> Vector a
+{-# INLINE (++) #-}
+(++) = (IV.++)
+
+-- | Create a copy of a vector. Useful when dealing with slices.
+copy :: Vector a -> Vector a
+{-# INLINE copy #-}
+copy = IV.copy
+
+-- Accessing individual elements
+-- -----------------------------
+
+-- | Indexing
+(!) :: Vector a -> Int -> a
+{-# INLINE (!) #-}
+(!) = (IV.!)
+
+-- | First element
+head :: Vector a -> a
+{-# INLINE head #-}
+head = IV.head
+
+-- | Last element
+last :: Vector a -> a
+{-# INLINE last #-}
+last = IV.last
+
+-- | Monadic indexing which can be strict in the vector while remaining lazy in
+-- the element
+indexM :: Monad m => Vector a -> Int -> m a
+{-# INLINE indexM #-}
+indexM = IV.indexM
+
+headM :: Monad m => Vector a -> m a
+{-# INLINE headM #-}
+headM = IV.headM
+
+lastM :: Monad m => Vector a -> m a
+{-# INLINE lastM #-}
+lastM = IV.lastM
+
+-- Subarrays
+-- ---------
+
+-- | Yield a part of the vector without copying it. Safer version of
+-- 'unsafeSlice'.
+slice :: Vector a -> Int   -- ^ starting index
+                  -> Int   -- ^ length
+                  -> Vector a
+{-# INLINE slice #-}
+slice = IV.slice
+
+-- | Yield all but the last element without copying.
+init :: Vector a -> Vector a
+{-# INLINE init #-}
+init = IV.init
+
+-- | All but the first element (without copying).
+tail :: Vector a -> Vector a
+{-# INLINE tail #-}
+tail = IV.tail
+
+-- | Yield the first @n@ elements without copying.
+take :: Int -> Vector a -> Vector a
+{-# INLINE take #-}
+take = IV.take
+
+-- | Yield all but the first @n@ elements without copying.
+drop :: Int -> Vector a -> Vector a
+{-# INLINE drop #-}
+drop = IV.drop
+
+-- Permutations
+-- ------------
+
+accum :: (a -> b -> a) -> Vector a -> [(Int,b)] -> Vector a
+{-# INLINE accum #-}
+accum = IV.accum
+
+(//) :: Vector a -> [(Int, a)] -> Vector a
+{-# INLINE (//) #-}
+(//) = (IV.//)
+
+update :: Vector a -> Vector (Int, a) -> Vector a
+{-# INLINE update #-}
+update = IV.update
+
+backpermute :: Vector a -> Vector Int -> Vector a
+{-# INLINE backpermute #-}
+backpermute = IV.backpermute
+
+reverse :: Vector a -> Vector a
+{-# INLINE reverse #-}
+reverse = IV.reverse
+
+-- Mapping
+-- -------
+
+-- | Map a function over a vector
+map :: (a -> b) -> Vector a -> Vector b
+{-# INLINE map #-}
+map = IV.map
+
+concatMap :: (a -> Vector b) -> Vector a -> Vector b
+{-# INLINE concatMap #-}
+concatMap = IV.concatMap
+
+-- Zipping/unzipping
+-- -----------------
+
+-- | Zip two vectors with the given function.
+zipWith :: (a -> b -> c) -> Vector a -> Vector b -> Vector c
+{-# INLINE zipWith #-}
+zipWith = IV.zipWith
+
+-- | Zip three vectors with the given function.
+zipWith3 :: (a -> b -> c -> d) -> Vector a -> Vector b -> Vector c -> Vector d
+{-# INLINE zipWith3 #-}
+zipWith3 = IV.zipWith3
+
+zip :: Vector a -> Vector b -> Vector (a, b)
+{-# INLINE zip #-}
+zip = IV.zip
+
+zip3 :: Vector a -> Vector b -> Vector c -> Vector (a, b, c)
+{-# INLINE zip3 #-}
+zip3 = IV.zip3
+
+unzip :: Vector (a, b) -> (Vector a, Vector b)
+{-# INLINE unzip #-}
+unzip = IV.unzip
+
+unzip3 :: Vector (a, b, c) -> (Vector a, Vector b, Vector c)
+{-# INLINE unzip3 #-}
+unzip3 = IV.unzip3
+
+-- Filtering
+-- ---------
+
+-- | Drop elements which do not satisfy the predicate
+filter :: (a -> Bool) -> Vector a -> Vector a
+{-# INLINE filter #-}
+filter = IV.filter
+
+-- | Yield the longest prefix of elements satisfying the predicate.
+takeWhile :: (a -> Bool) -> Vector a -> Vector a
+{-# INLINE takeWhile #-}
+takeWhile = IV.takeWhile
+
+-- | Drop the longest prefix of elements that satisfy the predicate.
+dropWhile :: (a -> Bool) -> Vector a -> Vector a
+{-# INLINE dropWhile #-}
+dropWhile = IV.dropWhile
+
+-- Searching
+-- ---------
+
+infix 4 `elem`
+-- | Check whether the vector contains an element
+elem :: Eq a => a -> Vector a -> Bool
+{-# INLINE elem #-}
+elem = IV.elem
+
+infix 4 `notElem`
+-- | Inverse of `elem`
+notElem :: Eq a => a -> Vector a -> Bool
+{-# INLINE notElem #-}
+notElem = IV.notElem
+
+-- | Yield 'Just' the first element matching the predicate or 'Nothing' if no
+-- such element exists.
+find :: (a -> Bool) -> Vector a -> Maybe a
+{-# INLINE find #-}
+find = IV.find
+
+-- | Yield 'Just' the index of the first element matching the predicate or
+-- 'Nothing' if no such element exists.
+findIndex :: (a -> Bool) -> Vector a -> Maybe Int
+{-# INLINE findIndex #-}
+findIndex = IV.findIndex
+
+-- Folding
+-- -------
+
+-- | Left fold
+foldl :: (a -> b -> a) -> a -> Vector b -> a
+{-# INLINE foldl #-}
+foldl = IV.foldl
+
+-- | Lefgt fold on non-empty vectors
+foldl1 :: (a -> a -> a) -> Vector a -> a
+{-# INLINE foldl1 #-}
+foldl1 = IV.foldl1
+
+-- | Left fold with strict accumulator
+foldl' :: (a -> b -> a) -> a -> Vector b -> a
+{-# INLINE foldl' #-}
+foldl' = IV.foldl'
+
+-- | Left fold on non-empty vectors with strict accumulator
+foldl1' :: (a -> a -> a) -> Vector a -> a
+{-# INLINE foldl1' #-}
+foldl1' = IV.foldl1'
+
+-- | Right fold
+foldr :: (a -> b -> b) -> b -> Vector a -> b
+{-# INLINE foldr #-}
+foldr = IV.foldr
+
+-- | Right fold on non-empty vectors
+foldr1 :: (a -> a -> a) -> Vector a -> a
+{-# INLINE foldr1 #-}
+foldr1 = IV.foldr1
+
+-- Specialised folds
+-- -----------------
+
+and :: Vector Bool -> Bool
+{-# INLINE and #-}
+and = IV.and
+
+or :: Vector Bool -> Bool
+{-# INLINE or #-}
+or = IV.or
+
+sum :: Num a => Vector a -> a
+{-# INLINE sum #-}
+sum = IV.sum
+
+product :: Num a => Vector a -> a
+{-# INLINE product #-}
+product = IV.product
+
+maximum :: Ord a => Vector a -> a
+{-# INLINE maximum #-}
+maximum = IV.maximum
+
+minimum :: Ord a => Vector a -> a
+{-# INLINE minimum #-}
+minimum = IV.minimum
+
+-- Unfolding
+-- ---------
+
+unfoldr :: (b -> Maybe (a, b)) -> b -> Vector a
+{-# INLINE unfoldr #-}
+unfoldr = IV.unfoldr
+
+-- Scans
+-- -----
+
+-- | Prefix scan
+prescanl :: (a -> b -> a) -> a -> Vector b -> Vector a
+{-# INLINE prescanl #-}
+prescanl = IV.prescanl
+
+-- | Prefix scan with strict accumulator
+prescanl' :: (a -> b -> a) -> a -> Vector b -> Vector a
+{-# INLINE prescanl' #-}
+prescanl' = IV.prescanl'
+
+-- Enumeration
+-- -----------
+
+enumFromTo :: Enum a => a -> a -> Vector a
+{-# INLINE enumFromTo #-}
+enumFromTo = IV.enumFromTo
+
+enumFromThenTo :: Enum a => a -> a -> a -> Vector a
+{-# INLINE enumFromThenTo #-}
+enumFromThenTo = IV.enumFromThenTo
+
+-- Conversion to/from lists
+-- ------------------------
+
+-- | Convert a vector to a list
+toList :: Vector a -> [a]
+{-# INLINE toList #-}
+toList = IV.toList
+
+-- | Convert a list to a vector
+fromList :: [a] -> Vector a
+{-# INLINE fromList #-}
+fromList = IV.fromList
 
diff --git a/Data/Vector/Fusion/Stream.hs b/Data/Vector/Fusion/Stream.hs
--- a/Data/Vector/Fusion/Stream.hs
+++ b/Data/Vector/Fusion/Stream.hs
@@ -16,7 +16,7 @@
 
 module Data.Vector.Fusion.Stream (
   -- * Types
-  Step(..), Stream, MStream, Id(..),
+  Step(..), Stream, MStream,
 
   -- * Size hints
   size, sized,
@@ -33,8 +33,11 @@
   -- * Substreams
   extract, init, tail, take, drop,
 
-  -- * Mapping and zipping
-  map, zipWith,
+  -- * Mapping
+  map, concatMap,
+  
+  -- * Zipping
+  zipWith, zipWith3,
 
   -- * Filtering
   filter, takeWhile, dropWhile,
@@ -45,8 +48,11 @@
   -- * Folding
   foldl, foldl1, foldl', foldl1', foldr, foldr1,
 
+  -- * Specialised folds
+  and, or,
+
   -- * Unfolding
-  unfold,
+  unfoldr,
 
   -- * Scans
   prescanl, prescanl',
@@ -59,6 +65,7 @@
 ) where
 
 import Data.Vector.Fusion.Stream.Size
+import Data.Vector.Fusion.Util
 import Data.Vector.Fusion.Stream.Monadic ( Step(..) )
 import qualified Data.Vector.Fusion.Stream.Monadic as M
 
@@ -66,23 +73,14 @@
                         replicate, (++),
                         head, last, (!!),
                         init, tail, take, drop,
-                        map, zipWith,
+                        map, concatMap,
+                        zipWith, zipWith3,
                         filter, takeWhile, dropWhile,
                         elem, notElem,
                         foldl, foldl1, foldr, foldr1,
+                        and, or,
                         mapM_ )
 
-
--- | Identity monad
-newtype Id a = Id { unId :: a }
-
-instance Functor Id where
-  fmap f (Id x) = Id (f x)
-
-instance Monad Id where
-  return     = Id
-  Id x >>= f = f x
-
 -- | The type of pure streams 
 type Stream = M.Stream Id
 
@@ -199,7 +197,7 @@
 {-# INLINE drop #-}
 drop = M.drop
 
--- Mapping/zipping
+-- Mapping
 -- ---------------
 
 -- | Map a function over a 'Stream'
@@ -207,11 +205,23 @@
 {-# INLINE map #-}
 map = M.map
 
+concatMap :: (a -> Stream b) -> Stream a -> Stream b
+{-# INLINE concatMap #-}
+concatMap = M.concatMap
+
+-- Zipping
+-- -------
+
 -- | Zip two 'Stream's with the given function
 zipWith :: (a -> b -> c) -> Stream a -> Stream b -> Stream c
 {-# INLINE zipWith #-}
 zipWith = M.zipWith
 
+-- | Zip three 'Stream's with the given function
+zipWith3 :: (a -> b -> c -> d) -> Stream a -> Stream b -> Stream c -> Stream d
+{-# INLINE zipWith3 #-}
+zipWith3 = M.zipWith3
+
 -- Filtering
 -- ---------
 
@@ -290,13 +300,24 @@
 {-# INLINE foldr1 #-}
 foldr1 f = unId . M.foldr1 f
 
+-- Specialised folds
+-- -----------------
+
+and :: Stream Bool -> Bool
+{-# INLINE and #-}
+and = unId . M.and
+
+or :: Stream Bool -> Bool
+{-# INLINE or #-}
+or = unId . M.or
+
 -- Unfolding
 -- ---------
 
 -- | Unfold
-unfold :: (s -> Maybe (a, s)) -> s -> Stream a
-{-# INLINE unfold #-}
-unfold = M.unfold
+unfoldr :: (s -> Maybe (a, s)) -> s -> Stream a
+{-# INLINE unfoldr #-}
+unfoldr = M.unfoldr
 
 -- Scans
 -- -----
diff --git a/Data/Vector/Fusion/Stream/Monadic.hs b/Data/Vector/Fusion/Stream/Monadic.hs
--- a/Data/Vector/Fusion/Stream/Monadic.hs
+++ b/Data/Vector/Fusion/Stream/Monadic.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE ExistentialQuantification, Rank2Types #-}
 
 -- |
 -- Module      : Data.Vector.Fusion.Stream.Monadic
@@ -32,8 +32,11 @@
   -- * Substreams
   extract, init, tail, take, drop,
 
-  -- * Mapping and zipping
-  map, mapM, mapM_, zipWith, zipWithM,
+  -- * Mapping
+  map, mapM, mapM_, trans, concatMap,
+  
+  -- * Zipping
+  zipWith, zipWithM, zipWith3, zipWith3M,
 
   -- * Filtering
   filter, filterM, takeWhile, takeWhileM, dropWhile, dropWhileM,
@@ -46,8 +49,11 @@
   foldl', foldlM', foldl1', foldl1M',
   foldr, foldrM, foldr1, foldr1M,
 
+  -- * Specialised folds
+  and, or, concatMapM,
+
   -- * Unfolding
-  unfold, unfoldM,
+  unfoldr, unfoldrM,
 
   -- * Scans
   prescanl, prescanlM, prescanl', prescanlM',
@@ -63,10 +69,12 @@
                         replicate, (++),
                         head, last, (!!),
                         init, tail, take, drop,
-                        map, mapM, mapM_, zipWith,
+                        map, mapM, mapM_, concatMap,
+                        zipWith, zipWith3,
                         filter, takeWhile, dropWhile,
                         elem, notElem,
-                        foldl, foldl1, foldr, foldr1 )
+                        foldl, foldl1, foldr, foldr1,
+                        and, or )
 import qualified Prelude
 
 -- | Result of taking a single step in a stream
@@ -193,7 +201,17 @@
 -- | Element at the given position
 (!!) :: Monad m => Stream m a -> Int -> m a
 {-# INLINE (!!) #-}
-s !! i = head (drop i s)
+Stream step s _ !! i | i < 0     = errorNegativeIndex "!!"
+                     | otherwise = loop s i
+  where
+    loop s i = i `seq`
+               do
+                 r <- step s
+                 case r of
+                   Yield x s' | i == 0    -> return x
+                              | otherwise -> loop s' (i-1)
+                   Skip    s'             -> loop s' i
+                   Done                   -> errorIndexOutOfRange "!!"
 
 -- Substreams
 -- ----------
@@ -215,7 +233,7 @@
                            case r of
                              Yield x s' -> Skip (Just x,  s')
                              Skip    s' -> Skip (Nothing, s')
-                             Done       -> Done  -- FIXME: should be an error
+                             Done       -> errorEmptyStream "init"
                          ) (step s)
 
     step' (Just x,  s) = liftM (\r -> 
@@ -235,7 +253,7 @@
                         case r of
                           Yield x s' -> Skip (Right s')
                           Skip    s' -> Skip (Left  s')
-                          Done       -> Done    -- FIXME: should be error?
+                          Done       -> errorEmptyStream "tail"
                       ) (step s)
 
     step' (Right s) = liftM (\r ->
@@ -281,8 +299,8 @@
                            ) (step s)
                      
 
--- Mapping/zipping
--- ---------------
+-- Mapping
+-- -------
 
 instance Monad m => Functor (Stream m) where
   {-# INLINE fmap #-}
@@ -318,6 +336,15 @@
                     Skip    s' -> mapM_go s'
                     Done       -> return ()
 
+-- | Transform a 'Stream' to use a different monad
+trans :: (Monad m, Monad m') => (forall a. m a -> m' a)
+                             -> Stream m a -> Stream m' a
+{-# INLINE_STREAM trans #-}
+trans f (Stream step s n) = Stream (f . step) s n
+
+-- Zipping
+-- -------
+
 -- | Zip two 'Stream's with the given function
 zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
 {-# INLINE zipWith #-}
@@ -347,6 +374,39 @@
                                  Skip    sb' -> return $ Skip (sa, sb', Just x)
                                  Done        -> return $ Done
 
+-- | Zip three 'Stream's with the given function
+zipWith3 :: Monad m => (a -> b -> c -> d) -> Stream m a -> Stream m b -> Stream m c -> Stream m d
+{-# INLINE zipWith3 #-}
+zipWith3 f = zipWith3M (\a b c -> return (f a b c))
+
+-- | Zip three 'Stream's with the given monadic function
+zipWith3M :: Monad m => (a -> b -> c -> m d) -> Stream m a -> Stream m b -> Stream m c -> Stream m d
+{-# INLINE_STREAM zipWith3M #-}
+zipWith3M f (Stream stepa sa na) (Stream stepb sb nb) (Stream stepc sc nc)
+  = Stream step (sa, sb, sc, Nothing) (smaller na (smaller nb nc))
+  where
+    {-# INLINE step #-}
+    step (sa, sb, sc, Nothing) = do
+        r <- stepa sa
+        return $ case r of
+            Yield x sa' -> Skip (sa', sb, sc, Just (x, Nothing))
+            Skip    sa' -> Skip (sa', sb, sc, Nothing)
+            Done        -> Done
+
+    step (sa, sb, sc, Just (x, Nothing)) = do
+        r <- stepb sb
+        return $ case r of
+            Yield y sb' -> Skip (sa, sb', sc, Just (x, Just y))
+            Skip    sb' -> Skip (sa, sb', sc, Just (x, Nothing))
+            Done        -> Done
+
+    step (sa, sb, sc, Just (x, Just y)) = do
+        r <- stepc sc
+        case r of
+            Yield z sc' -> f x y z >>= (\res -> return $ Yield res (sa, sb, sc', Nothing))
+            Skip    sc' -> return $ Skip (sa, sb, sc', Just (x, Just y))
+            Done        -> return $ Done
+
 -- Filtering
 -- ---------
 
@@ -614,18 +674,68 @@
                           Skip    s' -> foldr1M_go1 x s'
                           Done       -> return x
 
+-- Specialised folds
+-- -----------------
+
+and :: Monad m => Stream m Bool -> m Bool
+{-# INLINE_STREAM and #-}
+and (Stream step s _) = and_go s
+  where
+    and_go s = do
+                 r <- step s
+                 case r of
+                   Yield False _  -> return False
+                   Yield True  s' -> and_go s'
+                   Skip        s' -> and_go s'
+                   Done           -> return True
+
+or :: Monad m => Stream m Bool -> m Bool
+{-# INLINE_STREAM or #-}
+or (Stream step s _) = or_go s
+  where
+    or_go s = do
+                r <- step s
+                case r of
+                  Yield False s' -> or_go s'
+                  Yield True  _  -> return True
+                  Skip        s' -> or_go s'
+                  Done           -> return False
+
+concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
+{-# INLINE concatMap #-}
+concatMap f = concatMapM (return . f)
+
+concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
+{-# INLINE_STREAM concatMapM #-}
+concatMapM f (Stream step s _) = Stream concatMap_go (Left s) Unknown
+  where
+    concatMap_go (Left s) = do
+        r <- step s
+        case r of
+            Yield a s' -> do
+                b_stream <- f a
+                return $ Skip (Right (b_stream, s'))
+            Skip    s' -> return $ Skip (Left s')
+            Done       -> return Done
+    concatMap_go (Right (Stream inner_step inner_s sz, s)) = do
+        r <- inner_step inner_s
+        case r of
+            Yield b inner_s' -> return $ Yield b (Right (Stream inner_step inner_s' sz, s))
+            Skip    inner_s' -> return $ Skip (Right (Stream inner_step inner_s' sz, s))
+            Done             -> return $ Skip (Left s)
+
 -- Unfolding
 -- ---------
 
 -- | Unfold
-unfold :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a
-{-# INLINE_STREAM unfold #-}
-unfold f = unfoldM (return . f)
+unfoldr :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a
+{-# INLINE_STREAM unfoldr #-}
+unfoldr f = unfoldrM (return . f)
 
 -- | Unfold with a monadic function
-unfoldM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a
-{-# INLINE_STREAM unfoldM #-}
-unfoldM f s = Stream step s Unknown
+unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a
+{-# INLINE_STREAM unfoldrM #-}
+unfoldrM f s = Stream step s Unknown
   where
     {-# INLINE step #-}
     step s = liftM (\r ->
@@ -695,7 +805,16 @@
     step []     = return Done
 
 
+streamError :: String -> String -> a
+streamError fn msg = error $ "Data.Vector.Fusion.Stream.Monadic."
+                             Prelude.++ fn Prelude.++ ": " Prelude.++ msg
+
 errorEmptyStream :: String -> a
-errorEmptyStream s = error $ "Data.Vector.Fusion.Stream.Monadic."
-                        Prelude.++ s Prelude.++ ": empty stream"
+errorEmptyStream fn = streamError fn "empty stream"
+
+errorNegativeIndex :: String -> a
+errorNegativeIndex fn = streamError fn "negative index"
+
+errorIndexOutOfRange :: String -> a
+errorIndexOutOfRange fn = streamError fn "index out of range"
 
diff --git a/Data/Vector/Fusion/Util.hs b/Data/Vector/Fusion/Util.hs
new file mode 100644
--- /dev/null
+++ b/Data/Vector/Fusion/Util.hs
@@ -0,0 +1,35 @@
+-- |
+-- Module      : Data.Vector.Fusion.Util
+-- Copyright   : (c) Roman Leshchinskiy 2009
+-- License     : BSD-style
+--
+-- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : portable
+-- 
+-- Fusion-related utility types
+--
+
+module Data.Vector.Fusion.Util ( Id(..), Box(..) )
+where
+
+-- | Identity monad
+newtype Id a = Id { unId :: a }
+
+instance Functor Id where
+  fmap f (Id x) = Id (f x)
+
+instance Monad Id where
+  return     = Id
+  Id x >>= f = f x
+
+-- | Box monad
+data Box a = Box { unBox :: a }
+
+instance Functor Box where
+  fmap f (Box x) = Box (f x)
+
+instance Monad Box where
+  return      = Box
+  Box x >>= f = f x
+
diff --git a/Data/Vector/IVector.hs b/Data/Vector/IVector.hs
--- a/Data/Vector/IVector.hs
+++ b/Data/Vector/IVector.hs
@@ -19,38 +19,50 @@
   IVector,
 
   -- * Length information
-  length,
+  length, null,
 
   -- * Construction
-  empty, singleton, cons, snoc, replicate, (++),
+  empty, singleton, cons, snoc, replicate, (++), copy,
 
   -- * Accessing individual elements
-  (!), head, last,
+  (!), head, last, indexM, headM, lastM,
 
   -- * Subvectors
-  slice, extract, takeSlice, take, dropSlice, drop,
+  slice, init, tail, take, drop,
 
   -- * Permutations
-  (//), update, bpermute,
+  accum, (//), update, backpermute, reverse,
 
-  -- * Mapping and zipping
-  map, zipWith, zip,
+  -- * Mapping
+  map, concatMap,
 
+  -- * Zipping and unzipping
+  zipWith, zipWith3, zip, zip3, unzip, unzip3,
+
   -- * Comparisons
   eq, cmp,
 
   -- * Filtering
-  filter, takeWhileSlice, takeWhile, dropWhileSlice, dropWhile,
+  filter, takeWhile, dropWhile,
 
   -- * Searching
   elem, notElem, find, findIndex,
 
   -- * Folding
   foldl, foldl1, foldl', foldl1', foldr, foldr1,
+ 
+  -- * Specialised folds
+  and, or, sum, product, maximum, minimum,
 
+  -- * Unfolding
+  unfoldr,
+
   -- * Scans
   prescanl, prescanl',
 
+  -- * Enumeration
+  enumFromTo, enumFromThenTo,
+
   -- * Conversion to/from lists
   toList, fromList,
 
@@ -61,7 +73,7 @@
   new,
 
   -- * Unsafe functions
-  unsafeSlice, unsafeIndex,
+  unsafeSlice, unsafeIndexM,
 
   -- * Utility functions
   vlength, vnew
@@ -77,17 +89,21 @@
 import           Data.Vector.Fusion.Stream ( Stream, MStream )
 import qualified Data.Vector.Fusion.Stream.Monadic as MStream
 import           Data.Vector.Fusion.Stream.Size
+import           Data.Vector.Fusion.Util
 
 import Control.Exception ( assert )
 
-import Prelude hiding ( length,
+import Prelude hiding ( length, null,
                         replicate, (++),
                         head, last,
-                        init, tail, take, drop,
-                        map, zipWith, zip,
+                        init, tail, take, drop, reverse,
+                        map, concatMap,
+                        zipWith, zipWith3, zip, zip3, unzip, unzip3,
                         filter, takeWhile, dropWhile,
                         elem, notElem,
-                        foldl, foldl1, foldr, foldr1 )
+                        foldl, foldl1, foldr, foldr1,
+                        and, or, sum, product, maximum, minimum,
+                        enumFromTo, enumFromThenTo )
 
 -- | Class of immutable vectors.
 --
@@ -101,26 +117,28 @@
   -- | Yield a part of the vector without copying it. No range checks!
   unsafeSlice  :: v a -> Int -> Int -> v a
 
-  -- | Apply the given function to the element at the given position. This
-  -- interface prevents us from being too lazy. Suppose we had
+  -- | Yield the element at the given position in a monad. The monad allows us
+  -- to be strict in the vector if we want. Suppose we had
   --
-  -- > unsafeIndex' :: v a -> Int -> a
+  -- > unsafeIndex :: v a -> Int -> a
   --
   -- instead. Now, if we wanted to copy a vector, we'd do something like
   --
-  -- > copy mv v ... = ... unsafeWrite mv i (unsafeIndex' v i) ...
+  -- > copy mv v ... = ... unsafeWrite mv i (unsafeIndex v i) ...
   --
   -- For lazy vectors, the indexing would not be evaluated which means that we
   -- would retain a reference to the original vector in each element we write.
-  -- This would be bad!
+  -- This is not what we want!
   --
-  -- With 'unsafeIndex', we can do
+  -- With 'unsafeIndexM', we can do
   --
-  -- > copy mv v ... = ... unsafeIndex v i (unsafeWrite mv i) ...
+  -- > copy mv v ... = ... case unsafeIndexM v i of
+  --                         Box x -> unsafeWrite mv i x ...
   --
-  -- which does not have this problem.
+  -- which does not have this problem because indexing (but not the returned
+  -- element!) is evaluated immediately.
   --
-  unsafeIndex  :: v a -> Int -> (a -> b) -> b
+  unsafeIndexM  :: Monad m => v a -> Int -> m a
 
 -- Fusion
 -- ------
@@ -141,12 +159,12 @@
 -- | Convert a vector to a 'Stream'
 stream :: IVector v a => v a -> Stream a
 {-# INLINE_STREAM stream #-}
-stream v = v `seq` (Stream.unfold get 0 `Stream.sized` Exact n)
+stream v = v `seq` (Stream.unfoldr get 0 `Stream.sized` Exact n)
   where
     n = length v
 
     {-# INLINE get #-}
-    get i | i < n     = unsafeIndex v i $ \x -> Just (x, i+1)
+    get i | i < n     = case unsafeIndexM v i of Box x -> Just (x, i+1)
           | otherwise = Nothing
 
 -- | Create a vector from a 'Stream'
@@ -201,6 +219,17 @@
 
   #-}
 
+null :: IVector v a => v a -> Bool
+{-# INLINE_STREAM null #-}
+null v = vlength v == 0
+
+{-# RULES
+
+"null/unstream [IVector]" forall v s.
+  null (new' v (New.unstream s)) = Stream.null s
+
+  #-}
+
 -- Construction
 -- ------------
 
@@ -235,6 +264,18 @@
 {-# INLINE (++) #-}
 v ++ w = unstream (stream v Stream.++ stream w)
 
+-- | Create a copy of a vector. Useful when dealing with slices.
+copy :: IVector v a => v a -> v a
+{-# INLINE_STREAM copy #-}
+copy = unstream . stream
+
+{-# RULES
+
+"copy/unstream [IVector]" forall v s.
+  copy (new' v (New.unstream s)) = new' v (New.unstream s)
+
+ #-}
+
 -- Accessing individual elements
 -- -----------------------------
 
@@ -242,7 +283,7 @@
 (!) :: IVector v a => v a -> Int -> a
 {-# INLINE_STREAM (!) #-}
 v ! i = assert (i >= 0 && i < length v)
-      $ unsafeIndex v i id
+      $ unId (unsafeIndexM v i)
 
 -- | First element
 head :: IVector v a => v a -> a
@@ -267,6 +308,34 @@
 
  #-}
 
+-- | Monadic indexing which can be strict in the vector while remaining lazy in
+-- the element.
+indexM :: (IVector v a, Monad m) => v a -> Int -> m a
+{-# INLINE_STREAM indexM #-}
+indexM v i = assert (i >= 0 && i < length v)
+           $ unsafeIndexM v i
+
+headM :: (IVector v a, Monad m) => v a -> m a
+{-# INLINE_STREAM headM #-}
+headM v = indexM v 0
+
+lastM :: (IVector v a, Monad m) => v a -> m a
+{-# INLINE_STREAM lastM #-}
+lastM v = indexM v (length v - 1)
+
+{-# RULES
+
+"indexM/unstream [IVector]" forall v i s.
+  indexM (new' v (New.unstream s)) i = return (s Stream.!! i)
+
+"headM/unstream [IVector]" forall v s.
+  headM (new' v (New.unstream s)) = return (Stream.head s)
+
+"lastM/unstream [IVector]" forall v s.
+  lastM (new' v (New.unstream s)) = return (Stream.last s)
+
+ #-}
+
 -- Subarrays
 -- ---------
 
@@ -281,43 +350,56 @@
 slice v i n = assert (i >= 0 && n >= 0  && i+n <= length v)
             $ unsafeSlice v i n
 
--- | Copy @n@ elements starting at the given position to a new vector.
-extract :: IVector v a => v a -> Int  -- ^ starting index
-                              -> Int  -- ^ length
-                              -> v a
-{-# INLINE extract #-}
-extract v i n = unstream (Stream.extract (stream v) i n)
+-- | Yield all but the last element without copying.
+init :: IVector v a => v a -> v a
+{-# INLINE_STREAM init #-}
+init v = slice v 0 (length v - 1)
 
--- | Yield the first @n@ elements without copying.
-takeSlice :: IVector v a => Int -> v a -> v a
-{-# INLINE takeSlice #-}
-takeSlice n v = slice v 0 n
+-- | All but the first element (without copying).
+tail :: IVector v a => v a -> v a
+{-# INLINE_STREAM tail #-}
+tail v = slice v 1 (length v - 1)
 
--- | Copy the first @n@ elements to a new vector.
+-- | Yield the first @n@ elements without copying.
 take :: IVector v a => Int -> v a -> v a
-{-# INLINE take #-}
-take n = unstream . Stream.take n . stream
+{-# INLINE_STREAM take #-}
+take n v = slice v 0 (min n' (length v))
+  where n' = max n 0
 
 -- | Yield all but the first @n@ elements without copying.
-dropSlice :: IVector v a => Int -> v a -> v a
-{-# INLINE dropSlice #-}
-dropSlice n v = slice v n (length v - n)
-
--- | Copy all but the first @n@ elements to a new vectors.
 drop :: IVector v a => Int -> v a -> v a
-{-# INLINE drop #-}
-drop n = unstream . Stream.drop n . stream
+{-# INLINE_STREAM drop #-}
+drop n v = slice v (min n' len) (max 0 (len - n'))
+  where n' = max n 0
+        len = length v
 
 {-# RULES
 
-"slice/unstream [IVector]" forall v i n s.
-  slice (new' v (New.unstream s)) i n = extract (new' v (New.unstream s)) i n
+"slice/new [IVector]" forall v p i n.
+  slice (new' v p) i n = new' v (New.slice p i n)
 
+"init/new [IVector]" forall v p.
+  init (new' v p) = new' v (New.init p)
+
+"tail/new [IVector]" forall v p.
+  tail (new' v p) = new' v (New.tail p)
+
+"take/new [IVector]" forall n v p.
+  take n (new' v p) = new' v (New.take n p)
+
+"drop/new [IVector]" forall n v p.
+  drop n (new' v p) = new' v (New.drop n p)
+
   #-}
 
 -- Permutations
 -- ------------
 
+accum :: IVector v a => (a -> b -> a) -> v a -> [(Int,b)] -> v a
+{-# INLINE accum #-}
+accum f v us = new (New.accum f (New.unstream (stream v))
+                                (Stream.fromList us))
+
 (//) :: IVector v a => v a -> [(Int, a)] -> v a
 {-# INLINE (//) #-}
 v // us = new (New.update (New.unstream (stream v))
@@ -327,13 +409,26 @@
 {-# INLINE update #-}
 update v w = new (New.update (New.unstream (stream v)) (stream w))
 
-bpermute :: (IVector v a, IVector v Int) => v a -> v Int -> v a
-{-# INLINE bpermute #-}
-bpermute v is = v `seq` map (v!) is
+-- This somewhat non-intuitive definition ensures that the resulting vector
+-- does not retain references to the original one even if it is lazy in its
+-- elements. This would not be the case if we simply used
+--
+-- backpermute v is = map (v!) is
+backpermute :: (IVector v a, IVector v Int) => v a -> v Int -> v a
+{-# INLINE backpermute #-}
+backpermute v is = unstream
+                 . MStream.trans (Id . unBox)
+                 . MStream.mapM (indexM v)
+                 . MStream.trans (Box . unId)
+                 $ stream is
 
--- Mapping/zipping
--- ---------------
+reverse :: (IVector v a) => v a -> v a
+{-# INLINE reverse #-}
+reverse = new . New.reverse . New.unstream . stream
 
+-- Mapping
+-- -------
+
 -- | Map a function over a vector
 map :: (IVector v a, IVector v b) => (a -> b) -> v a -> v b
 {-# INLINE map #-}
@@ -349,15 +444,39 @@
 
  #-}
 
+concatMap :: (IVector v a, IVector v b) => (a -> v b) -> v a -> v b
+{-# INLINE concatMap #-}
+concatMap f = unstream . Stream.concatMap (stream . f) . stream
+
+-- Zipping/unzipping
+-- -----------------
+
 -- | Zip two vectors with the given function.
 zipWith :: (IVector v a, IVector v b, IVector v c) => (a -> b -> c) -> v a -> v b -> v c
 {-# INLINE zipWith #-}
 zipWith f xs ys = unstream (Stream.zipWith f (stream xs) (stream ys))
 
+-- | Zip three vectors with the given function.
+zipWith3 :: (IVector v a, IVector v b, IVector v c, IVector v d) => (a -> b -> c -> d) -> v a -> v b -> v c -> v d
+{-# INLINE zipWith3 #-}
+zipWith3 f xs ys zs = unstream (Stream.zipWith3 f (stream xs) (stream ys) (stream zs))
+
 zip :: (IVector v a, IVector v b, IVector v (a,b)) => v a -> v b -> v (a, b)
 {-# INLINE zip #-}
 zip = zipWith (,)
 
+zip3 :: (IVector v a, IVector v b, IVector v c, IVector v (a, b, c)) => v a -> v b -> v c -> v (a, b, c)
+{-# INLINE zip3 #-}
+zip3 = zipWith3 (,,)
+
+unzip :: (IVector v a, IVector v b, IVector v (a,b)) => v (a, b) -> (v a, v b)
+{-# INLINE unzip #-}
+unzip xs = (map fst xs, map snd xs)
+
+unzip3 :: (IVector v a, IVector v b, IVector v c, IVector v (a, b, c)) => v (a, b, c) -> (v a, v b, v c)
+{-# INLINE unzip3 #-}
+unzip3 xs = (map (\(a, b, c) -> a) xs, map (\(a, b, c) -> b) xs, map (\(a, b, c) -> c) xs)
+
 -- Comparisons
 -- -----------
 
@@ -377,44 +496,16 @@
 {-# INLINE filter #-}
 filter f = unstream . inplace (MStream.filter f) . stream
 
--- | Yield the longest prefix of elements satisfying the predicate without
--- copying.
-takeWhileSlice :: IVector v a => (a -> Bool) -> v a -> v a
-{-# INLINE_STREAM takeWhileSlice #-}
-takeWhileSlice f v = case findIndex (not . f) v of
-                       Just n  -> takeSlice n v
-                       Nothing -> v
-
--- | Copy the longest prefix of elements satisfying the predicate to a new
--- vector
+-- | Yield the longest prefix of elements satisfying the predicate.
 takeWhile :: IVector v a => (a -> Bool) -> v a -> v a
 {-# INLINE takeWhile #-}
 takeWhile f = unstream . Stream.takeWhile f . stream
 
--- | Drop the longest prefix of elements that satisfy the predicate without
--- copying
-dropWhileSlice :: IVector v a => (a -> Bool) -> v a -> v a
-{-# INLINE_STREAM dropWhileSlice #-}
-dropWhileSlice f v = case findIndex (not . f) v of
-                       Just n  -> dropSlice n v
-                       Nothing -> v
-
--- | Drop the longest prefix of elements that satisfy the predicate and copy
--- the rest to a new vector.
+-- | Drop the longest prefix of elements that satisfy the predicate.
 dropWhile :: IVector v a => (a -> Bool) -> v a -> v a
 {-# INLINE dropWhile #-}
 dropWhile f = unstream . Stream.dropWhile f . stream
 
-{-# RULES
-
-"takeWhileSlice/unstream" forall v f s.
-  takeWhileSlice f (new' v (New.unstream s)) = takeWhile f (new' v (New.unstream s))
-
-"dropWhileSlice/unstream" forall v f s.
-  dropWhileSlice f (new' v (New.unstream s)) = dropWhile f (new' v (New.unstream s))
-
- #-}
-
 -- Searching
 -- ---------
 
@@ -475,6 +566,40 @@
 {-# INLINE foldr1 #-}
 foldr1 f = Stream.foldr1 f . stream
 
+-- Specialised folds
+-- -----------------
+
+and :: IVector v Bool => v Bool -> Bool
+{-# INLINE and #-}
+and = Stream.and . stream
+
+or :: IVector v Bool => v Bool -> Bool
+{-# INLINE or #-}
+or = Stream.or . stream
+
+sum :: (IVector v a, Num a) => v a -> a
+{-# INLINE sum #-}
+sum = Stream.foldl' (+) 0 . stream
+
+product :: (IVector v a, Num a) => v a -> a
+{-# INLINE product #-}
+product = Stream.foldl' (*) 1 . stream
+
+maximum :: (IVector v a, Ord a) => v a -> a
+{-# INLINE maximum #-}
+maximum = Stream.foldl1' max . stream
+
+minimum :: (IVector v a, Ord a) => v a -> a
+{-# INLINE minimum #-}
+minimum = Stream.foldl1' min . stream
+
+-- Unfolding
+-- ---------
+
+unfoldr :: IVector v a => (b -> Maybe (a, b)) -> b -> v a
+{-# INLINE unfoldr #-}
+unfoldr f = unstream . Stream.unfoldr f
+
 -- Scans
 -- -----
 
@@ -508,6 +633,27 @@
 
  #-}
 
+
+-- Enumeration
+-- -----------
+
+enumFromTo :: (IVector v a, Enum a) => a -> a -> v a
+{-# INLINE enumFromTo #-}
+enumFromTo from to = from `seq` to `seq` unfoldr enumFromTo_go (fromEnum from)
+  where
+    to_i = fromEnum to
+    enumFromTo_go i | i <= to_i = Just (toEnum i, i + 1)
+                    | otherwise = Nothing
+
+enumFromThenTo :: (IVector v a, Enum a) => a -> a -> a -> v a
+{-# INLINE enumFromThenTo #-}
+enumFromThenTo from next to = from `seq` next `seq` to `seq` unfoldr enumFromThenTo_go from_i
+  where
+    from_i = fromEnum from
+    to_i = fromEnum to
+    step_i = fromEnum next - from_i
+    enumFromThenTo_go i | i <= to_i = Just (toEnum i, i + step_i)
+                        | otherwise = Nothing
 
 -- | Convert a vector to a list
 toList :: IVector v a => v a -> [a]
diff --git a/Data/Vector/MVector.hs b/Data/Vector/MVector.hs
--- a/Data/Vector/MVector.hs
+++ b/Data/Vector/MVector.hs
@@ -18,7 +18,7 @@
 
   slice, new, newWith, read, write, copy, grow,
   unstream, transform,
-  update, reverse
+  accum, update, reverse
 ) where
 
 import qualified Data.Vector.Fusion.Stream      as Stream
@@ -169,7 +169,7 @@
 
 mstream :: MVector v m a => v a -> MStream m a
 {-# INLINE mstream #-}
-mstream v = v `seq` (MStream.unfoldM get 0 `MStream.sized` Exact n)
+mstream v = v `seq` (MStream.unfoldrM get 0 `MStream.sized` Exact n)
   where
     n = length v
 
@@ -229,12 +229,18 @@
                                  . double2Int
                                  $ int2Double (length v) * gROWTH_FACTOR
 
+accum :: MVector v m a => (a -> b -> a) -> v a -> Stream (Int, b) -> m ()
+{-# INLINE accum #-}
+accum f v s = Stream.mapM_ upd s
+  where
+    {-# INLINE upd #-}
+    upd (i,b) = do
+                  a <- read v i
+                  write v i (f a b)
+
 update :: MVector v m a => v a -> Stream (Int, a) -> m ()
 {-# INLINE update #-}
-update v s = Stream.mapM_ put s
-  where
-    {-# INLINE put #-}
-    put (i, x) = write v i x
+update = accum (const id)
 
 reverse :: MVector v m a => v a -> m ()
 {-# INLINE reverse #-}
@@ -245,5 +251,6 @@
                                  y <- unsafeRead v j
                                  unsafeWrite v i y
                                  unsafeWrite v j x
+                                 reverse_loop (i + 1) (j - 1)
     reverse_loop _ _ = return ()
 
diff --git a/Data/Vector/MVector/New.hs b/Data/Vector/MVector/New.hs
--- a/Data/Vector/MVector/New.hs
+++ b/Data/Vector/MVector/New.hs
@@ -3,11 +3,12 @@
 #include "phases.h"
 
 module Data.Vector.MVector.New (
-  New(..), run, unstream, transform, update, reverse
+  New(..), run, unstream, transform, accum, update, reverse,
+  slice, init, tail, take, drop
 ) where
 
 import qualified Data.Vector.MVector as MVector
-import           Data.Vector.MVector ( MVector )
+import           Data.Vector.MVector ( MVector, MVectorPure )
 
 import           Data.Vector.Fusion.Stream ( Stream, MStream )
 import qualified Data.Vector.Fusion.Stream as Stream
@@ -15,7 +16,7 @@
 import qualified Data.Vector.Fusion.Stream.Monadic as MStream
 
 import Control.Monad  ( liftM )
-import Prelude hiding ( reverse, map, filter )
+import Prelude hiding ( init, tail, take, drop, reverse, map, filter )
 
 newtype New a = New (forall m mv. MVector mv m a => m (mv a))
 
@@ -23,6 +24,10 @@
 {-# INLINE run #-}
 run (New p) = p
 
+apply :: (forall mv a. MVectorPure mv a => mv a -> mv a) -> New a -> New a
+{-# INLINE apply #-}
+apply f (New p) = New (liftM f p)
+
 modify :: New a -> (forall m mv. MVector mv m a => mv a -> m ()) -> New a
 {-# INLINE modify #-}
 modify (New p) q = New (do { v <- p; q v; return v })
@@ -43,7 +48,55 @@
          p .
   transform f (transform g p) = transform (f . g) p
 
+"transform/unstream [New]"
+  forall (f :: forall m. Monad m => MStream m a -> MStream m a)
+         s.
+  transform f (unstream s) = unstream (f s)
+
  #-}
+
+slice :: New a -> Int -> Int -> New a
+{-# INLINE_STREAM slice #-}
+slice m i n = apply (\v -> MVector.slice v i n) m
+
+init :: New a -> New a
+{-# INLINE_STREAM init #-}
+init m = apply (\v -> MVector.slice v 0 (MVector.length v - 1)) m
+
+tail :: New a -> New a
+{-# INLINE_STREAM tail #-}
+tail m = apply (\v -> MVector.slice v 1 (MVector.length v - 1)) m
+
+take :: Int -> New a -> New a
+{-# INLINE_STREAM take #-}
+take n m = apply (\v -> MVector.slice v 0 (min n (MVector.length v))) m
+
+drop :: Int -> New a -> New a
+{-# INLINE_STREAM drop #-}
+drop n m = apply (\v -> MVector.slice v n (max 0 (MVector.length v - n))) m
+
+{-# RULES
+
+"slice/unstream [New]" forall s i n.
+  slice (unstream s) i n = unstream (Stream.extract s i n)
+
+"init/unstream [New]" forall s.
+  init (unstream s) = unstream (Stream.init s)
+
+"tail/unstream [New]" forall s.
+  tail (unstream s) = unstream (Stream.tail s)
+
+"take/unstream [New]" forall n s.
+  take n (unstream s) = unstream (Stream.take n s)
+
+"drop/unstream [New]" forall n s.
+  drop n (unstream s) = unstream (Stream.drop n s)
+
+  #-}
+
+accum :: (a -> b -> a) -> New a -> Stream (Int, b) -> New a
+{-# INLINE_STREAM accum #-}
+accum f m s = modify m (\v -> MVector.accum f v s)
 
 update :: New a -> Stream (Int, a) -> New a
 {-# INLINE_STREAM update #-}
diff --git a/Data/Vector/Mutable.hs b/Data/Vector/Mutable.hs
deleted file mode 100644
--- a/Data/Vector/Mutable.hs
+++ /dev/null
@@ -1,77 +0,0 @@
-{-# LANGUAGE MagicHash, UnboxedTuples, MultiParamTypeClasses, FlexibleInstances #-}
-
--- |
--- Module      : Data.Vector.Mutable
--- Copyright   : (c) Roman Leshchinskiy 2008
--- License     : BSD-style
---
--- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
--- Stability   : experimental
--- Portability : non-portable
--- 
--- Mutable boxed vectors.
---
-
-module Data.Vector.Mutable ( Vector(..) )
-where
-
-import qualified Data.Vector.MVector as MVector
-import           Data.Vector.MVector ( MVector, MVectorPure )
-
-import GHC.Prim ( MutableArray#,
-                  newArray#, readArray#, writeArray#, sameMutableArray#, (+#) )
-
-import GHC.ST   ( ST(..) )
-
-import GHC.Base ( Int(..) )
-
--- | Mutable boxed vectors. They live in the 'ST' monad.
-data Vector s a = Vector {-# UNPACK #-} !Int
-                         {-# UNPACK #-} !Int
-                                        (MutableArray# s a)
-
-instance MVectorPure (Vector s) a where
-  length (Vector _ n _) = n
-  unsafeSlice (Vector i _ arr#) j m = Vector (i+j) m arr#
-
-  {-# INLINE overlaps #-}
-  overlaps (Vector i m arr1#) (Vector j n arr2#)
-    = sameMutableArray# arr1# arr2#
-      && (between i j (j+n) || between j i (i+m))
-    where
-      between x y z = x >= y && x < z
-
-
-instance MVector (Vector s) (ST s) a where
-  {-# INLINE unsafeNew #-}
-  unsafeNew = unsafeNew
-
-  {-# INLINE unsafeNewWith #-}
-  unsafeNewWith = unsafeNewWith
-
-  {-# INLINE unsafeRead #-}
-  unsafeRead (Vector (I# i#) _ arr#) (I# j#) = ST (readArray# arr# (i# +# j#))
-
-  {-# INLINE unsafeWrite #-}
-  unsafeWrite (Vector (I# i#) _ arr#) (I# j#) x = ST (\s# ->
-      case writeArray# arr# (i# +# j#) x s# of s2# -> (# s2#, () #)
-    )
-
-  {-# INLINE clear #-}
-  clear v = MVector.set v uninitialised
-
-
-uninitialised :: a
-uninitialised = error "Data.Vector.Mutable: uninitialised elemen t"
-
-unsafeNew :: Int -> ST s (Vector s a)
-{-# INLINE unsafeNew #-}
-unsafeNew n = unsafeNewWith n uninitialised
-
-unsafeNewWith :: Int -> a -> ST s (Vector s a)
-{-# INLINE unsafeNewWith #-}
-unsafeNewWith (I# n#) x = ST (\s# ->
-    case newArray# n# x s# of
-      (# s2#, arr# #) -> (# s2#, Vector 0 (I# n#) arr# #)
-  )
-
diff --git a/Data/Vector/Mutable/IO.hs b/Data/Vector/Mutable/IO.hs
new file mode 100644
--- /dev/null
+++ b/Data/Vector/Mutable/IO.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
+
+-- |
+-- Module      : Data.Vector.Mutable.IO
+-- Copyright   : (c) Roman Leshchinskiy 2009
+-- License     : BSD-style
+--
+-- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable
+-- 
+-- Mutable boxed vectors in the IO monad.
+--
+
+module Data.Vector.Mutable.IO ( Vector(..) )
+where
+
+import           Data.Vector.MVector ( MVector(..), MVectorPure(..) )
+import qualified Data.Vector.Mutable.ST as STV
+
+import GHC.Base   ( RealWorld )
+import GHC.ST     ( ST(..) )
+import GHC.IOBase ( IO(..) )
+
+import Prelude hiding ( length )
+
+-- | IO-based mutable vectors
+newtype Vector a = Vector (STV.Vector RealWorld a)
+
+instance MVectorPure Vector a where
+  {-# INLINE length #-}
+  length (Vector v) = length v
+
+  {-# INLINE unsafeSlice #-}
+  unsafeSlice (Vector v) j m = Vector (unsafeSlice v j m)
+
+  {-# INLINE overlaps #-}
+  overlaps (Vector v1) (Vector v2) = overlaps v1 v2
+
+instance MVector Vector IO a where
+  {-# INLINE unsafeNew #-}
+  unsafeNew n = Vector `fmap` stToIO (unsafeNew n)
+
+  {-# INLINE unsafeNewWith #-}
+  unsafeNewWith n x = Vector `fmap` stToIO (unsafeNewWith n x)
+
+  {-# INLINE unsafeRead #-}
+  unsafeRead (Vector v) i = stToIO (unsafeRead v i)
+
+  {-# INLINE unsafeWrite #-}
+  unsafeWrite (Vector v) i x = stToIO (unsafeWrite v i x)
+
+  {-# INLINE clear #-}
+  clear (Vector v) = stToIO (clear v)
+
+stToIO :: ST RealWorld a -> IO a
+stToIO (ST m) = IO m
+
diff --git a/Data/Vector/Mutable/ST.hs b/Data/Vector/Mutable/ST.hs
new file mode 100644
--- /dev/null
+++ b/Data/Vector/Mutable/ST.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE MagicHash, UnboxedTuples, MultiParamTypeClasses, FlexibleInstances #-}
+
+-- |
+-- Module      : Data.Vector.Mutable.ST
+-- Copyright   : (c) Roman Leshchinskiy 2008
+-- License     : BSD-style
+--
+-- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable
+-- 
+-- Mutable boxed vectors in the ST monad.
+--
+
+module Data.Vector.Mutable.ST ( Vector(..) )
+where
+
+import qualified Data.Vector.MVector as MVector
+import           Data.Vector.MVector ( MVector, MVectorPure )
+
+import GHC.Prim ( MutableArray#,
+                  newArray#, readArray#, writeArray#, sameMutableArray#, (+#) )
+
+import GHC.ST   ( ST(..) )
+
+import GHC.Base ( Int(..) )
+
+-- | Mutable boxed vectors. They live in the 'ST' monad.
+data Vector s a = Vector {-# UNPACK #-} !Int
+                         {-# UNPACK #-} !Int
+                                        (MutableArray# s a)
+
+instance MVectorPure (Vector s) a where
+  length (Vector _ n _) = n
+  unsafeSlice (Vector i _ arr#) j m = Vector (i+j) m arr#
+
+  {-# INLINE overlaps #-}
+  overlaps (Vector i m arr1#) (Vector j n arr2#)
+    = sameMutableArray# arr1# arr2#
+      && (between i j (j+n) || between j i (i+m))
+    where
+      between x y z = x >= y && x < z
+
+
+instance MVector (Vector s) (ST s) a where
+  {-# INLINE unsafeNew #-}
+  unsafeNew = unsafeNew
+
+  {-# INLINE unsafeNewWith #-}
+  unsafeNewWith = unsafeNewWith
+
+  {-# INLINE unsafeRead #-}
+  unsafeRead (Vector (I# i#) _ arr#) (I# j#) = ST (readArray# arr# (i# +# j#))
+
+  {-# INLINE unsafeWrite #-}
+  unsafeWrite (Vector (I# i#) _ arr#) (I# j#) x = ST (\s# ->
+      case writeArray# arr# (i# +# j#) x s# of s2# -> (# s2#, () #)
+    )
+
+  {-# INLINE clear #-}
+  clear v = MVector.set v uninitialised
+
+
+uninitialised :: a
+uninitialised = error "Data.Vector.Mutable: uninitialised elemen t"
+
+unsafeNew :: Int -> ST s (Vector s a)
+{-# INLINE unsafeNew #-}
+unsafeNew n = unsafeNewWith n uninitialised
+
+unsafeNewWith :: Int -> a -> ST s (Vector s a)
+{-# INLINE unsafeNewWith #-}
+unsafeNewWith (I# n#) x = ST (\s# ->
+    case newArray# n# x s# of
+      (# s2#, arr# #) -> (# s2#, Vector 0 (I# n#) arr# #)
+  )
+
diff --git a/Data/Vector/Unboxed.hs b/Data/Vector/Unboxed.hs
--- a/Data/Vector/Unboxed.hs
+++ b/Data/Vector/Unboxed.hs
@@ -13,11 +13,57 @@
 --
 
 module Data.Vector.Unboxed (
-  Vector(..), module Data.Vector.IVector
+  Vector,
+
+  -- * Length information
+  length, null,
+
+  -- * Construction
+  empty, singleton, cons, snoc, replicate, (++), copy,
+
+  -- * Accessing individual elements
+  (!), head, last,
+
+  -- * Subvectors
+  slice, init, tail, take, drop,
+
+  -- * Permutations
+  accum, (//), backpermute, reverse,
+
+  -- * Mapping
+  map, concatMap,
+
+  -- * Zipping and unzipping
+  zipWith, zipWith3,
+
+  -- * Filtering
+  filter, takeWhile, dropWhile,
+
+  -- * Searching
+  elem, notElem, find, findIndex,
+
+  -- * Folding
+  foldl, foldl1, foldl', foldl1', foldr, foldr1,
+
+  -- * Specialised folds
+  {-and, or,-} sum, product, maximum, minimum,
+
+  -- * Unfolding
+  unfoldr,
+
+  -- * Scans
+  prescanl, prescanl',
+
+  -- * Enumeration
+  enumFromTo, enumFromThenTo,
+
+  -- * Conversion to/from lists
+  toList, fromList
 ) where
 
-import           Data.Vector.IVector
-import qualified Data.Vector.Unboxed.Mutable as Mut
+import           Data.Vector.IVector ( IVector(..) )
+import qualified Data.Vector.IVector            as IV
+import qualified Data.Vector.Unboxed.Mutable.ST as Mut
 import           Data.Vector.Unboxed.Unbox
 
 import Control.Monad.ST ( runST )
@@ -26,11 +72,28 @@
 import GHC.Prim ( ByteArray#, unsafeFreezeByteArray#, (+#) )
 import GHC.Base ( Int(..) )
 
+import Prelude hiding ( length, null,
+                        replicate, (++),
+                        head, last,
+                        init, tail, take, drop, reverse,
+                        map, concatMap,
+                        zipWith, zipWith3, zip, zip3, unzip, unzip3,
+                        filter, takeWhile, dropWhile,
+                        elem, notElem,
+                        foldl, foldl1, foldr, foldr1,
+                        and, or, sum, product, minimum, maximum,
+                        enumFromTo, enumFromThenTo )
+
+import qualified Prelude
+
 -- | Unboxed vectors
 data Vector a = Vector {-# UNPACK #-} !Int
                        {-# UNPACK #-} !Int
                                       ByteArray#
 
+instance (Show a, Unbox a) => Show (Vector a) where
+    show = (Prelude.++ " :: Data.Vector.Unboxed.Vector") . ("fromList " Prelude.++) . show . toList
+
 instance Unbox a => IVector Vector a where
   {-# INLINE vnew #-}
   vnew init = runST (do
@@ -44,14 +107,310 @@
   {-# INLINE unsafeSlice #-}
   unsafeSlice (Vector i _ arr#) j n = Vector (i+j) n arr#
 
-  {-# INLINE unsafeIndex #-}
-  unsafeIndex (Vector (I# i#) _ arr#) (I# j#) f = f (at# arr# (i# +# j#))
+  {-# INLINE unsafeIndexM #-}
+  unsafeIndexM (Vector (I# i#) _ arr#) (I# j#) = return (at# arr# (i# +# j#))
 
 instance (Unbox a, Eq a) => Eq (Vector a) where
   {-# INLINE (==) #-}
-  (==) = eq
+  (==) = IV.eq
 
 instance (Unbox a, Ord a) => Ord (Vector a) where
   {-# INLINE compare #-}
-  compare = cmp
+  compare = IV.cmp
+
+-- Length
+-- ------
+
+length :: Unbox a => Vector a -> Int
+{-# INLINE length #-}
+length = IV.length
+
+null :: Unbox a => Vector a -> Bool
+{-# INLINE null #-}
+null = IV.null
+
+-- Construction
+-- ------------
+
+-- | Empty vector
+empty :: Unbox a => Vector a
+{-# INLINE empty #-}
+empty = IV.empty
+
+-- | Vector with exaclty one element
+singleton :: Unbox a => a -> Vector a
+{-# INLINE singleton #-}
+singleton = IV.singleton
+
+-- | Vector of the given length with the given value in each position
+replicate :: Unbox a => Int -> a -> Vector a
+{-# INLINE replicate #-}
+replicate = IV.replicate
+
+-- | Prepend an element
+cons :: Unbox a => a -> Vector a -> Vector a
+{-# INLINE cons #-}
+cons = IV.cons
+
+-- | Append an element
+snoc :: Unbox a => Vector a -> a -> Vector a
+{-# INLINE snoc #-}
+snoc = IV.snoc
+
+infixr 5 ++
+-- | Concatenate two vectors
+(++) :: Unbox a => Vector a -> Vector a -> Vector a
+{-# INLINE (++) #-}
+(++) = (IV.++)
+
+-- | Create a copy of a vector. Useful when dealing with slices.
+copy :: Unbox a => Vector a -> Vector a
+{-# INLINE copy #-}
+copy = IV.copy
+
+-- Accessing individual elements
+-- -----------------------------
+
+-- | Indexing
+(!) :: Unbox a => Vector a -> Int -> a
+{-# INLINE (!) #-}
+(!) = (IV.!)
+
+-- | First element
+head :: Unbox a => Vector a -> a
+{-# INLINE head #-}
+head = IV.head
+
+-- | Last element
+last :: Unbox a => Vector a -> a
+{-# INLINE last #-}
+last = IV.last
+
+-- Subarrays
+-- ---------
+
+-- | Yield a part of the vector without copying it. Safer version of
+-- 'unsafeSlice'.
+slice :: Unbox a => Vector a -> Int   -- ^ starting index
+                             -> Int   -- ^ length
+                             -> Vector a
+{-# INLINE slice #-}
+slice = IV.slice
+
+-- | Yield all but the last element without copying.
+init :: Unbox a => Vector a -> Vector a
+{-# INLINE init #-}
+init = IV.init
+
+-- | All but the first element (without copying).
+tail :: Unbox a => Vector a -> Vector a
+{-# INLINE tail #-}
+tail = IV.tail
+
+-- | Yield the first @n@ elements without copying.
+take :: Unbox a => Int -> Vector a -> Vector a
+{-# INLINE take #-}
+take = IV.take
+
+-- | Yield all but the first @n@ elements without copying.
+drop :: Unbox a => Int -> Vector a -> Vector a
+{-# INLINE drop #-}
+drop = IV.drop
+
+-- Permutations
+-- ------------
+
+accum :: Unbox a => (a -> b -> a) -> Vector a -> [(Int,b)] -> Vector a
+{-# INLINE accum #-}
+accum = IV.accum
+
+(//) :: Unbox a => Vector a -> [(Int, a)] -> Vector a
+{-# INLINE (//) #-}
+(//) = (IV.//)
+
+backpermute :: Unbox a => Vector a -> Vector Int -> Vector a
+{-# INLINE backpermute #-}
+backpermute = IV.backpermute
+
+reverse :: Unbox a => Vector a -> Vector a
+{-# INLINE reverse #-}
+reverse = IV.reverse
+
+-- Mapping
+-- -------
+
+-- | Map a function over a vector
+map :: (Unbox a, Unbox b) => (a -> b) -> Vector a -> Vector b
+{-# INLINE map #-}
+map = IV.map
+
+concatMap :: (Unbox a, Unbox b) => (a -> Vector b) -> Vector a -> Vector b
+{-# INLINE concatMap #-}
+concatMap = IV.concatMap
+
+-- Zipping/unzipping
+-- -----------------
+
+-- | Zip two vectors with the given function.
+zipWith :: (Unbox a, Unbox b, Unbox c)
+        => (a -> b -> c) -> Vector a -> Vector b -> Vector c
+{-# INLINE zipWith #-}
+zipWith = IV.zipWith
+
+-- | Zip three vectors with the given function.
+zipWith3 :: (Unbox a, Unbox b, Unbox c, Unbox d)
+         => (a -> b -> c -> d) -> Vector a -> Vector b -> Vector c -> Vector d
+{-# INLINE zipWith3 #-}
+zipWith3 = IV.zipWith3
+
+-- Filtering
+-- ---------
+
+-- | Drop elements which do not satisfy the predicate
+filter :: Unbox a => (a -> Bool) -> Vector a -> Vector a
+{-# INLINE filter #-}
+filter = IV.filter
+
+-- | Yield the longest prefix of elements satisfying the predicate.
+takeWhile :: Unbox a => (a -> Bool) -> Vector a -> Vector a
+{-# INLINE takeWhile #-}
+takeWhile = IV.takeWhile
+
+-- | Drop the longest prefix of elements that satisfy the predicate.
+dropWhile :: Unbox a => (a -> Bool) -> Vector a -> Vector a
+{-# INLINE dropWhile #-}
+dropWhile = IV.dropWhile
+
+-- Searching
+-- ---------
+
+infix 4 `elem`
+-- | Check whether the vector contains an element
+elem :: (Unbox a, Eq a) => a -> Vector a -> Bool
+{-# INLINE elem #-}
+elem = IV.elem
+
+infix 4 `notElem`
+-- | Inverse of `elem`
+notElem :: (Unbox a, Eq a) => a -> Vector a -> Bool
+{-# INLINE notElem #-}
+notElem = IV.notElem
+
+-- | Yield 'Just' the first element matching the predicate or 'Nothing' if no
+-- such element exists.
+find :: Unbox a => (a -> Bool) -> Vector a -> Maybe a
+{-# INLINE find #-}
+find = IV.find
+
+-- | Yield 'Just' the index of the first element matching the predicate or
+-- 'Nothing' if no such element exists.
+findIndex :: Unbox a => (a -> Bool) -> Vector a -> Maybe Int
+{-# INLINE findIndex #-}
+findIndex = IV.findIndex
+
+-- Folding
+-- -------
+
+-- | Left fold
+foldl :: Unbox b => (a -> b -> a) -> a -> Vector b -> a
+{-# INLINE foldl #-}
+foldl = IV.foldl
+
+-- | Lefgt fold on non-empty vectors
+foldl1 :: Unbox a => (a -> a -> a) -> Vector a -> a
+{-# INLINE foldl1 #-}
+foldl1 = IV.foldl1
+
+-- | Left fold with strict accumulator
+foldl' :: Unbox b => (a -> b -> a) -> a -> Vector b -> a
+{-# INLINE foldl' #-}
+foldl' = IV.foldl'
+
+-- | Left fold on non-empty vectors with strict accumulator
+foldl1' :: Unbox a => (a -> a -> a) -> Vector a -> a
+{-# INLINE foldl1' #-}
+foldl1' = IV.foldl1'
+
+-- | Right fold
+foldr :: Unbox a => (a -> b -> b) -> b -> Vector a -> b
+{-# INLINE foldr #-}
+foldr = IV.foldr
+
+-- | Right fold on non-empty vectors
+foldr1 :: Unbox a => (a -> a -> a) -> Vector a -> a
+{-# INLINE foldr1 #-}
+foldr1 = IV.foldr1
+
+-- Specialised folds
+-- -----------------
+
+{-
+and :: Vector Bool -> Bool
+{-# INLINE and #-}
+and = IV.and
+
+or :: Vector Bool -> Bool
+{-# INLINE or #-}
+or = IV.or
+-}
+
+sum :: (Unbox a, Num a) => Vector a -> a
+{-# INLINE sum #-}
+sum = IV.sum
+
+product :: (Unbox a, Num a) => Vector a -> a
+{-# INLINE product #-}
+product = IV.product
+
+maximum :: (Unbox a, Ord a) => Vector a -> a
+{-# INLINE maximum #-}
+maximum = IV.maximum
+
+minimum :: (Unbox a, Ord a) => Vector a -> a
+{-# INLINE minimum #-}
+minimum = IV.minimum
+
+-- Unfolding
+-- ---------
+
+unfoldr :: Unbox a => (b -> Maybe (a, b)) -> b -> Vector a
+{-# INLINE unfoldr #-}
+unfoldr = IV.unfoldr
+
+-- Scans
+-- -----
+
+-- | Prefix scan
+prescanl :: (Unbox a, Unbox b) => (a -> b -> a) -> a -> Vector b -> Vector a
+{-# INLINE prescanl #-}
+prescanl = IV.prescanl
+
+-- | Prefix scan with strict accumulator
+prescanl' :: (Unbox a, Unbox b) => (a -> b -> a) -> a -> Vector b -> Vector a
+{-# INLINE prescanl' #-}
+prescanl' = IV.prescanl'
+
+-- Enumeration
+-- -----------
+
+enumFromTo :: (Unbox a, Enum a) => a -> a -> Vector a
+{-# INLINE enumFromTo #-}
+enumFromTo = IV.enumFromTo
+
+enumFromThenTo :: (Unbox a, Enum a) => a -> a -> a -> Vector a
+{-# INLINE enumFromThenTo #-}
+enumFromThenTo = IV.enumFromThenTo
+
+-- Conversion to/from lists
+-- ------------------------
+
+-- | Convert a vector to a list
+toList :: Unbox a => Vector a -> [a]
+{-# INLINE toList #-}
+toList = IV.toList
+
+-- | Convert a list to a vector
+fromList :: Unbox a => [a] -> Vector a
+{-# INLINE fromList #-}
+fromList = IV.fromList
 
diff --git a/Data/Vector/Unboxed/Mutable.hs b/Data/Vector/Unboxed/Mutable.hs
deleted file mode 100644
--- a/Data/Vector/Unboxed/Mutable.hs
+++ /dev/null
@@ -1,63 +0,0 @@
-{-# LANGUAGE MagicHash, UnboxedTuples, MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables #-}
-
--- |
--- Module      : Data.Vector.Unboxed.Mutable
--- Copyright   : (c) Roman Leshchinskiy 2008
--- License     : BSD-style
---
--- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
--- Stability   : experimental
--- Portability : non-portable
--- 
--- Mutable unboxed vectors based on 'Unbox'.
---
-
-module Data.Vector.Unboxed.Mutable ( Vector(..) )
-where
-
-import qualified Data.Vector.MVector as MVector
-import           Data.Vector.MVector ( MVector, MVectorPure )
-import           Data.Vector.Unboxed.Unbox
-
-import GHC.Prim ( MutableByteArray#,
-                  newByteArray#, sameMutableByteArray#, (+#) )
-
-import GHC.ST   ( ST(..) )
-
-import GHC.Base ( Int(..) )
-
--- | Mutable unboxed vectors. They live in the 'ST' monad.
-data Vector s a = Vector {-# UNPACK #-} !Int
-                         {-# UNPACK #-} !Int
-                                        (MutableByteArray# s)
-
-instance Unbox a => MVectorPure (Vector s) a where
-  length (Vector _ n _) = n
-  unsafeSlice (Vector i _ arr#) j m = Vector (i+j) m arr#
-
-  {-# INLINE overlaps #-}
-  overlaps (Vector i m arr1#) (Vector j n arr2#)
-    = sameMutableByteArray# arr1# arr2#
-      && (between i j (j+n) || between j i (i+m))
-    where
-      between x y z = x >= y && x < z
-
-
-instance Unbox a => MVector (Vector s) (ST s) a where
-  {-# INLINE unsafeNew #-}
-  unsafeNew (I# n#) = ST (\s# ->
-      case newByteArray# (size# (undefined :: a) n#) s# of
-        (# s2#, arr# #) -> (# s2#, Vector 0 (I# n#) arr# #)
-    )
-
-  {-# INLINE unsafeRead #-}
-  unsafeRead (Vector (I# i#) _ arr#) (I# j#) = ST (read# arr# (i# +# j#))
-
-  {-# INLINE unsafeWrite #-}
-  unsafeWrite (Vector (I# i#) _ arr#) (I# j#) x = ST (\s# ->
-      case write# arr# (i# +# j#) x s# of s2# -> (# s2#, () #)
-    )
-
-  {-# INLINE clear #-}
-  clear _ = return ()
-
diff --git a/Data/Vector/Unboxed/Mutable/IO.hs b/Data/Vector/Unboxed/Mutable/IO.hs
new file mode 100644
--- /dev/null
+++ b/Data/Vector/Unboxed/Mutable/IO.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
+
+-- |
+-- Module      : Data.Vector.Mutable.Unboxed.IO
+-- Copyright   : (c) Roman Leshchinskiy 2009
+-- License     : BSD-style
+--
+-- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable
+-- 
+-- Mutable unboxed vectors in the IO monad.
+--
+
+module Data.Vector.Unboxed.Mutable.IO ( Vector(..) )
+where
+
+import           Data.Vector.MVector ( MVector(..), MVectorPure(..) )
+import qualified Data.Vector.Unboxed.Mutable.ST as STV
+import           Data.Vector.Unboxed.Unbox ( Unbox )
+
+import GHC.Base   ( RealWorld )
+import GHC.ST     ( ST(..) )
+import GHC.IOBase ( IO(..) )
+
+import Prelude hiding ( length )
+
+-- | IO-based mutable vectors
+newtype Vector a = Vector (STV.Vector RealWorld a)
+
+instance Unbox a => MVectorPure Vector a where
+  {-# INLINE length #-}
+  length (Vector v) = length v
+
+  {-# INLINE unsafeSlice #-}
+  unsafeSlice (Vector v) j m = Vector (unsafeSlice v j m)
+
+  {-# INLINE overlaps #-}
+  overlaps (Vector v1) (Vector v2) = overlaps v1 v2
+
+instance Unbox a => MVector Vector IO a where
+  {-# INLINE unsafeNew #-}
+  unsafeNew n = Vector `fmap` stToIO (unsafeNew n)
+
+  {-# INLINE unsafeNewWith #-}
+  unsafeNewWith n x = Vector `fmap` stToIO (unsafeNewWith n x)
+
+  {-# INLINE unsafeRead #-}
+  unsafeRead (Vector v) i = stToIO (unsafeRead v i)
+
+  {-# INLINE unsafeWrite #-}
+  unsafeWrite (Vector v) i x = stToIO (unsafeWrite v i x)
+
+  {-# INLINE clear #-}
+  clear (Vector v) = stToIO (clear v)
+
+stToIO :: ST RealWorld a -> IO a
+stToIO (ST m) = IO m
+
diff --git a/Data/Vector/Unboxed/Mutable/ST.hs b/Data/Vector/Unboxed/Mutable/ST.hs
new file mode 100644
--- /dev/null
+++ b/Data/Vector/Unboxed/Mutable/ST.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE MagicHash, UnboxedTuples, MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables #-}
+
+-- |
+-- Module      : Data.Vector.Unboxed.Mutable.ST
+-- Copyright   : (c) Roman Leshchinskiy 2008
+-- License     : BSD-style
+--
+-- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable
+-- 
+-- Mutable unboxed vectors based on 'Unbox' in the ST monad.
+--
+
+module Data.Vector.Unboxed.Mutable.ST ( Vector(..) )
+where
+
+import qualified Data.Vector.MVector as MVector
+import           Data.Vector.MVector ( MVector, MVectorPure )
+import           Data.Vector.Unboxed.Unbox
+
+import GHC.Prim ( MutableByteArray#,
+                  newByteArray#, sameMutableByteArray#, (+#) )
+
+import GHC.ST   ( ST(..) )
+
+import GHC.Base ( Int(..) )
+
+-- | Mutable unboxed vectors. They live in the 'ST' monad.
+data Vector s a = Vector {-# UNPACK #-} !Int
+                         {-# UNPACK #-} !Int
+                                        (MutableByteArray# s)
+
+instance Unbox a => MVectorPure (Vector s) a where
+  length (Vector _ n _) = n
+  unsafeSlice (Vector i _ arr#) j m = Vector (i+j) m arr#
+
+  {-# INLINE overlaps #-}
+  overlaps (Vector i m arr1#) (Vector j n arr2#)
+    = sameMutableByteArray# arr1# arr2#
+      && (between i j (j+n) || between j i (i+m))
+    where
+      between x y z = x >= y && x < z
+
+
+instance Unbox a => MVector (Vector s) (ST s) a where
+  {-# INLINE unsafeNew #-}
+  unsafeNew (I# n#) = ST (\s# ->
+      case newByteArray# (size# (undefined :: a) n#) s# of
+        (# s2#, arr# #) -> (# s2#, Vector 0 (I# n#) arr# #)
+    )
+
+  {-# INLINE unsafeRead #-}
+  unsafeRead (Vector (I# i#) _ arr#) (I# j#) = ST (read# arr# (i# +# j#))
+
+  {-# INLINE unsafeWrite #-}
+  unsafeWrite (Vector (I# i#) _ arr#) (I# j#) x = ST (\s# ->
+      case write# arr# (i# +# j#) x s# of s2# -> (# s2#, () #)
+    )
+
+  {-# INLINE clear #-}
+  clear _ = return ()
+
diff --git a/Data/Vector/Unboxed/Unbox.hs b/Data/Vector/Unboxed/Unbox.hs
--- a/Data/Vector/Unboxed/Unbox.hs
+++ b/Data/Vector/Unboxed/Unbox.hs
@@ -22,11 +22,15 @@
 import GHC.Float (
     Float(..), Double(..)
   )
+import GHC.Word (
+    Word(..)
+  )
 
 import GHC.Prim (
     ByteArray#, MutableByteArray#, State#,
 
     Int#, indexIntArray#,    readIntArray#,    writeIntArray#,
+          indexWordArray#,   readWordArray#,   writeWordArray#,
           indexFloatArray#,  readFloatArray#,  writeFloatArray#,
           indexDoubleArray#, readDoubleArray#, writeDoubleArray#
   )
@@ -49,6 +53,14 @@
 
   -- | Store the given element at the given position
   write# :: MutableByteArray# s -> Int# -> a -> State# s -> State# s
+
+instance Unbox Word where
+  size# _                   = wORD_SCALE
+  at#    arr# i#            = W# (indexWordArray# arr# i#)
+  read#  arr# i# s#         = case readWordArray# arr# i# s# of
+                                (# s1#, n# #) -> (# s1#, W# n# #)
+  write# arr# i# (W# n#) s# = writeWordArray# arr# i# n# s#
+
 
 instance Unbox Int where
   size#  _                  = wORD_SCALE
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,4 @@
-Copyright (c) 2001-2002, Manuel M T Chakravarty & Gabriele Keller
-Copyright (c) 2006-2007, Manuel M T Chakravarty & Roman Leshchinskiy
+Copyright (c) 2008-2009, Roman Leshchinskiy
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/vector.cabal b/vector.cabal
--- a/vector.cabal
+++ b/vector.cabal
@@ -1,5 +1,5 @@
 Name:           vector
-Version:        0.2
+Version:        0.3
 License:        BSD3
 License-File:   LICENSE
 Author:         Roman Leshchinskiy
@@ -10,18 +10,23 @@
 Synopsis:       Efficient Arrays
 Description:
         .
-        An efficient implementation of Int-indexed arrays with a powerful
-        loop fusion framework.
+        An efficient but highly experimental implementation of Int-indexed
+        arrays with a powerful loop fusion framework.
         .
-        This code is highly experimental and for the most part untested. Use
-        at your own risk!
 
 Cabal-Version:  >= 1.2
 Build-Type:     Simple
 
+Flag EnableAssertions
+  Description: Enable assertions that check parameters to functions are reasonable.
+               These will impose a moderate performance cost on users of the library,
+               with the benefit that you get reasonable errors rather than segmentation faults!
+  Default:     False
+
 Library
   Extensions: CPP
   Exposed-Modules:
+        Data.Vector.Fusion.Util
         Data.Vector.Fusion.Stream.Size
         Data.Vector.Fusion.Stream.Monadic
         Data.Vector.Fusion.Stream
@@ -31,10 +36,12 @@
         Data.Vector.IVector
 
         Data.Vector.Unboxed.Unbox
-        Data.Vector.Unboxed.Mutable
+        Data.Vector.Unboxed.Mutable.ST
+        Data.Vector.Unboxed.Mutable.IO
         Data.Vector.Unboxed
 
-        Data.Vector.Mutable
+        Data.Vector.Mutable.ST
+        Data.Vector.Mutable.IO
         Data.Vector
   Include-Dirs:
         include
@@ -42,6 +49,23 @@
   Install-Includes:
         phases.h
 
-  Build-Depends: base, array, ghc-prim,
+  Build-Depends: base >= 2 && < 4, array, ghc-prim,
                  ghc >= 6.9
 
+-- -finline-if-enough-args is ESSENTIAL. If we don't have this the partial application
+-- of e.g. Stream.Monadic.++ to the monad dictionary at the use site in Stream.++ causes
+-- it to be fruitlessly inlined. This in turn leads to a huge RHS for Stream.++, so it
+-- doesn't get inlined at the final call site and fusion fails to occur.
+  Ghc-Options: -finline-if-enough-args
+  
+-- It's probably a good idea to compile the library with -O2 as well. However, it's probably
+-- not as essential as you think because most of the optimisation occurs when the library
+-- functions from here are inlined into the user programs (which SHOULD be compiled with -O2!).
+--
+-- We have to fiddle with the assertion stuff at this point too because -O2 implies -fno-ignore-asserts,
+-- meaning that their relative ordering is CRUCIAL. Setting them together guarantees it.
+  if flag(enableassertions)
+    -- Asserts are ignored by default at -O1 or higher
+    Ghc-Options: -O2 -fno-ignore-asserts
+  else
+    Ghc-Options: -O2
