diff --git a/Changes.md b/Changes.md
--- a/Changes.md
+++ b/Changes.md
@@ -1,3 +1,7 @@
+* 0.0.3
+ - Add support for 7.10
+ - Upgrades STM version to 2.5 (which made changes in their API from Int to Natural for some parameters)
+
 * 0.0.2
  - Update async to 2.2. This version of async changed the exceptions thrown, see
    https://hackage.haskell.org/package/async-2.2/changelog for more details.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Twine
+Entwine
 =====
 
 Concurrency tools.
diff --git a/entwine.cabal b/entwine.cabal
--- a/entwine.cabal
+++ b/entwine.cabal
@@ -1,5 +1,5 @@
 name:                  entwine
-version:               0.0.2
+version:               0.0.3
 license:               BSD3
 license-file:          LICENSE
 author:                Ambiata <info@ambiata.com>
@@ -11,9 +11,9 @@
 category:              System
 cabal-version:         >= 1.8
 build-type:            Simple
-description:           entwine - Concurrency tools
+description:           Entwine provides concurrency types and tools for building correct software.
 
-tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4
+tested-with: GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4
 extra-source-files:
   README.md
   Changes.md
@@ -26,11 +26,14 @@
   build-depends:
                        base                            >= 3          && < 5
                      , async                           == 2.2.*
+                     , clock                           >= 0.6        && < 0.8
                      , containers                      >= 0.5        && < 0.7
                      , exceptions                      >= 0.6        && < 0.9
                      , monad-loops                     == 0.4.*
+                     , retry
                      , SafeSemaphore                   == 0.10.*
-                     , stm                             == 2.4.*
+                     , semigroups                      == 0.18.*
+                     , stm                             == 2.5.*
                      , text                            == 1.2.*
                      , time                            >= 1.4        && < 1.9
                      , transformers                    >= 0.3        && < 0.6
@@ -54,8 +57,8 @@
                        Entwine.Data.Pin
                        Entwine.Data.Queue
                        Entwine.Loop
-                       Entwine.Parallel
                        Entwine.P
+                       Entwine.Parallel
                        Entwine.Snooze
                        Entwine.Guard
 
@@ -119,6 +122,7 @@
                      , process                         >= 1.2        && < 1.7
                      , text
                      , time
+                     , transformers
                      , transformers-either
                      , QuickCheck                      == 2.10.*
                      , quickcheck-instances            == 0.3.*
diff --git a/src/Entwine/Async.hs b/src/Entwine/Async.hs
--- a/src/Entwine/Async.hs
+++ b/src/Entwine/Async.hs
@@ -14,8 +14,7 @@
 import           Control.Concurrent.Async (async, cancel, wait, AsyncCancelled)
 import           Control.Concurrent.STM (atomically, orElse, retry)
 import           Control.Monad.Catch (catches, throwM, Handler(..))
-import           Control.Monad.IO.Class (liftIO)
-import           Control.Monad.Trans.Either
+import           Control.Monad.Trans.Either (EitherT, left)
 
 import           Data.IORef (newIORef, readIORef, writeIORef)
 import qualified Data.Text as T
@@ -25,7 +24,7 @@
 
 import           System.IO (IO)
 
-data AsyncTimeout =
+newtype AsyncTimeout =
   AsyncTimeout Duration
   deriving (Eq, Show)
 
@@ -46,12 +45,12 @@
   e <- liftIO $ waitEither a s
   case e of
     Left a' ->
-      pure $ a'
+      pure a'
     Right _ -> do
       liftIO $ writeIORef r True
       liftIO $ cancel a
       (liftIO $ wait a)
-        `catches` [ Handler (\ (ax :: AsyncCancelled) -> do
+        `catches` [ Handler (\ (ax :: AsyncCancelled) ->
                                 liftIO (readIORef r) >>=
                                   bool (liftIO $ throwM ax) (left $ AsyncTimeout d)
                             )]
diff --git a/src/Entwine/Data/Duration.hs b/src/Entwine/Data/Duration.hs
--- a/src/Entwine/Data/Duration.hs
+++ b/src/Entwine/Data/Duration.hs
@@ -1,6 +1,8 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 module Entwine.Data.Duration (
+  -- * Types
     Duration
+  -- * Functions
   , microseconds
   , milliseconds
   , seconds
diff --git a/src/Entwine/Data/Finalizer.hs b/src/Entwine/Data/Finalizer.hs
--- a/src/Entwine/Data/Finalizer.hs
+++ b/src/Entwine/Data/Finalizer.hs
@@ -5,7 +5,7 @@
 
 import           Entwine.P
 
-import           System.IO
+import           System.IO (IO)
 
 
 -- |
diff --git a/src/Entwine/Data/Gate.hs b/src/Entwine/Data/Gate.hs
--- a/src/Entwine/Data/Gate.hs
+++ b/src/Entwine/Data/Gate.hs
@@ -1,6 +1,8 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 module Entwine.Data.Gate (
+  -- * Types
     Gate
+  -- * Functions
   , newGate
   , isOpen
   , close
@@ -10,7 +12,7 @@
 
 import           Entwine.P
 
-import           System.IO
+import           System.IO (IO)
 
 -- |
 -- A gate is an abstract type, representing a simple barrier
diff --git a/src/Entwine/Data/Parallel.hs b/src/Entwine/Data/Parallel.hs
--- a/src/Entwine/Data/Parallel.hs
+++ b/src/Entwine/Data/Parallel.hs
@@ -1,7 +1,9 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 module Entwine.Data.Parallel (
+  -- * Types
     Workers
   , Result
+  -- * Functions
   , newResult
   , emptyResult
   , addResult
@@ -15,12 +17,15 @@
   ) where
 
 import           Control.Concurrent.Async (Async, cancel, wait)
-import           Control.Concurrent.MVar
+import           Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, modifyMVar
+                                         , modifyMVar_, readMVar)
 
 import           Entwine.P
 
-import           System.IO
+import           System.IO (IO)
 
+-- |
+--
 newtype Result a =
   Result (MVar a)
 
@@ -35,12 +40,14 @@
 
 addResult :: Monad m => Result (m a) -> m a -> IO (m a)
 addResult (Result r) w =
-  modifyMVar r (\x -> pure $ ((x >> w), x))
+  modifyMVar r (\x -> pure (x >> w, x))
 
 getResult :: Result a -> IO a
 getResult (Result r) =
   readMVar r
 
+-- |
+--
 newtype Workers a =
   Workers (MVar [Async a])
 
@@ -57,14 +64,14 @@
 getWorkers (Workers w) =
   readMVar w
 
-addWorker :: (Workers a) -> Async a -> IO ()
+addWorker :: Workers a -> Async a -> IO ()
 addWorker (Workers w) r =
   modifyMVar_ w $ pure . (:) r
 
-waitForWorkers :: (Workers a) -> IO ()
+waitForWorkers :: Workers a -> IO ()
 waitForWorkers (Workers w) =
   readMVar w >>= mapM_ wait
 
-waitForWorkers' :: (Workers a) -> IO [a]
+waitForWorkers' :: Workers a -> IO [a]
 waitForWorkers' (Workers w) =
   readMVar w >>= mapM wait
diff --git a/src/Entwine/Data/Pin.hs b/src/Entwine/Data/Pin.hs
--- a/src/Entwine/Data/Pin.hs
+++ b/src/Entwine/Data/Pin.hs
@@ -1,17 +1,20 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 module Entwine.Data.Pin (
+  -- * Types
     Pin
+  -- * Functions
   , newPin
   , checkPin
   , waitForPin
   , pullPin
   ) where
 
-import           Control.Concurrent.MVar
+import           Control.Concurrent.MVar (MVar, newEmptyMVar, tryTakeMVar
+                                         , readMVar, tryPutMVar)
 
 import           Entwine.P
 
-import           System.IO
+import           System.IO (IO)
 
 -- |
 -- A pin is an abstract type, representing a simple barrier
diff --git a/src/Entwine/Data/Queue.hs b/src/Entwine/Data/Queue.hs
--- a/src/Entwine/Data/Queue.hs
+++ b/src/Entwine/Data/Queue.hs
@@ -8,21 +8,31 @@
   , isQueueEmpty
   ) where
 
-import           Control.Concurrent.STM.TBQueue ( TBQueue, newTBQueue, tryReadTBQueue
-                                                , readTBQueue, writeTBQueue, isEmptyTBQueue)
+import           Control.Concurrent.STM.TBQueue (TBQueue, isEmptyTBQueue, newTBQueue, readTBQueue, tryReadTBQueue,
+                                                 writeTBQueue)
 
+import           Entwine.P
+
 import           GHC.Conc (atomically)
 
-import           Entwine.P
+import           Numeric.Natural (Natural)
 
-import           System.IO
+import           System.IO (IO)
 
+-- |
+--
+-- A queue is an abstract type, representing a simple bounded FIFO channel
+-- that can only have its state queried in a non-blocking manner.
+--
+-- This useful for implementing things like passing data between a
+-- producer and consumer process with back presure.
+--
 newtype Queue a =
   Queue {
       queue :: TBQueue a
     }
 
-newQueue :: Int -> IO (Queue a)
+newQueue :: Natural -> IO (Queue a)
 newQueue i =
   atomically $ Queue <$> newTBQueue i
 
diff --git a/src/Entwine/Guard.hs b/src/Entwine/Guard.hs
--- a/src/Entwine/Guard.hs
+++ b/src/Entwine/Guard.hs
@@ -1,7 +1,9 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 module Entwine.Guard (
+  -- * Types
     TerminationAction (..)
   , TerminationHandler (..)
+  -- * Functions
   , guarded
   , repeatedly
   ) where
diff --git a/src/Entwine/Loop.hs b/src/Entwine/Loop.hs
--- a/src/Entwine/Loop.hs
+++ b/src/Entwine/Loop.hs
@@ -4,8 +4,6 @@
     loop
   ) where
 
-import           Control.Monad.IO.Class (MonadIO, liftIO)
-
 import           Entwine.Data.Gate
 import           Entwine.Data.Duration
 import           Entwine.P
diff --git a/src/Entwine/Parallel.hs b/src/Entwine/Parallel.hs
--- a/src/Entwine/Parallel.hs
+++ b/src/Entwine/Parallel.hs
@@ -1,42 +1,46 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ExplicitForAll #-}
-{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 module Entwine.Parallel (
+  -- * Types
     RunError (..)
+  -- * Functions
   , renderRunError
-  , consume_
   , consume
+  , consume_
   , waitEitherBoth
   ) where
 
 
 import           Control.Concurrent (threadDelay)
-import           Control.Concurrent.Async (async, cancel, poll, waitBoth, wait, waitEither)
+import           Control.Concurrent.Async (async, cancel, poll, wait, waitBoth, waitEither)
 import           Control.Concurrent.MSem (new, signal)
 import qualified Control.Concurrent.MSem as M
-import           Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
-import           Control.Monad.Catch
-import           Control.Monad.IO.Class
+import           Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
+import           Control.Monad.Catch (Exception (..), SomeException, catch, catchAll, finally, throwM)
 import           Control.Monad.Loops (untilM_)
-import           Control.Monad.Trans.Either
+import           Control.Monad.Trans.Either (EitherT, pattern EitherT, bimapEitherT, runEitherT)
 
 import qualified Data.Text as T
-import           Data.Typeable
+import           Data.Typeable (Typeable)
 
 import           Entwine.Async (waitEitherBoth)
 import           Entwine.Data.Parallel
 import           Entwine.Data.Queue
 import           Entwine.P
 
-import           System.IO
+import           Numeric.Natural (Natural)
 
+import           System.IO (IO)
+
 -- | Provide a producer and an action to be run across the result
 --   of that producer in parallel.
---
+--   For a version that doesn't ignore the results see 'Entwine.Parallel.consume'.
 --
 --   Common usage:
+--
 --   @
 --     let producer :: Address -> Queue Address -> IO ()
 --         producer prefix q =
@@ -45,7 +49,7 @@
 --     consume producer 100 (\(a :: Address) -> doThis)
 --   @
 --
-consume_ :: MonadIO m => (Queue b -> IO a) -> Int -> (b -> EitherT e IO ()) -> EitherT (RunError e) m a
+consume_ :: MonadIO m => (Queue b -> IO a) -> Natural -> (b -> EitherT e IO ()) -> EitherT (RunError e) m a
 consume_ pro fork action = EitherT . liftIO $ do
   q <- newQueue fork
 
@@ -67,7 +71,7 @@
        threadDelay 1000 {-- 1 ms --}
        p <- poll producer
        e <- isQueueEmpty q
-       pure $ (isJust p) && e
+       pure $ isJust p && e
 
   submitter <- async $ untilM_ spawn check
 
@@ -82,7 +86,7 @@
         waitForWorkers workers
         pure i
 
-  (waiter >>= \i -> getResult result >>= pure . second (const $ i))
+  (waiter >>= \i -> getResult result >>= pure . second (const i))
     `catchAll` (\z ->
       failWorkers workers >>
         getResult result >>= \w ->
@@ -106,8 +110,23 @@
 
 instance Exception EarlyTermination
 
-consume :: forall a b c e . (Queue a -> IO b) -> Int -> (a -> IO (Either e c)) -> IO (Either (RunError e) (b, [c]))
-consume pro fork action = flip catchAll (pure . Left . BlowUpError) $ do
+-- | Provide a producer and an action to be run across the results
+--   of that producer in parallel and collect the results.
+--   For a version that ignores the results see 'Entwine.Parallel.consume_'.
+--
+--   Common usage:
+--
+--  @
+--     let producer :: Address -> Queue Address -> IO Int
+--         producer prefix q =
+--           list' prefix $$ writeQueue q
+--
+--     consume producer 100 (\(a :: Address) -> doThis)
+--  @
+--
+consume :: MonadIO m => (Queue b -> IO a) -> Natural -> (b -> IO (Either e c))
+        -> m (Either (RunError e) (a, [c]))
+consume pro fork action = liftIO . flip catchAll (pure . Left . BlowUpError) $ do
   q <- newQueue fork -- not fork
   producer <- async $ pro q
   workers <- (emptyWorkers :: IO (Workers c))
@@ -136,7 +155,7 @@
        threadDelay 1000 {-- 1 ms --}
        p <- poll producer
        e <- isQueueEmpty q
-       pure $ (isJust p) && e
+       pure $ isJust p && e
 
   submitter <- async $ untilM_ spawn check
 
@@ -145,8 +164,8 @@
 
         ws <- liftIO $ getWorkers workers
         ii <- mapM (bimapEitherT WorkerError id . EitherT . waitEither terminator) ws
-        pure $ (i, ii)
+        pure (i, ii)
 
   waiter `catch` (\(_ :: EarlyTermination) ->
     failWorkers workers >>
-      (Left . WorkerError) <$> wait terminator)
+      Left . WorkerError <$> wait terminator)
diff --git a/src/Entwine/Snooze.hs b/src/Entwine/Snooze.hs
--- a/src/Entwine/Snooze.hs
+++ b/src/Entwine/Snooze.hs
@@ -1,16 +1,18 @@
 {-# LANGUAGE NoImplicitPrelude #-}
 module Entwine.Snooze (
     snooze
-  , module Entwine.Data.Duration
+  , module X
   ) where
 
-import           Control.Concurrent
+import           Control.Concurrent (threadDelay)
 
 import           Entwine.P
-import           Entwine.Data.Duration
+import           Entwine.Data.Duration as X
 
 import           System.IO (IO)
 
+-- | Snooze for a short duration
+--
 snooze :: Duration -> IO ()
 snooze =
   threadDelay . toMicroseconds
diff --git a/test/Test/Entwine/Parallel.hs b/test/Test/Entwine/Parallel.hs
--- a/test/Test/Entwine/Parallel.hs
+++ b/test/Test/Entwine/Parallel.hs
@@ -79,9 +79,15 @@
       work = const $ pure ()
 
   r <- runEitherT $ consume_ pro 1 work
-  pure $ (isRight r) === True
+  pure $ isRight r === True
 
+prop_empty_producer_consume = testIO $ do
+  let pro = const . pure . Right $ (1 :: Int)
+      work = const . pure . Right $ (1 :: Int)
 
+  r <- runEitherT $ consume pro 1 work
+  pure $ isRight r === True
+
 return []
 tests :: IO Bool
-tests = $quickCheckAll
+tests = $forAllProperties $ quickCheckWithResult (stdArgs { maxSuccess = 10 })
diff --git a/test/bench.hs b/test/bench.hs
--- a/test/bench.hs
+++ b/test/bench.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 import           Control.Concurrent
-import           Control.Monad.IO.Class (liftIO)
 import           Control.Monad.Trans.Either
 
 import           Criterion.Main
@@ -9,7 +8,6 @@
 
 import           Entwine.Data
 import           Entwine.Parallel
-
 import           Entwine.P
 
 import           System.IO
