diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
@@ -0,0 +1,110 @@
+Release Notes
+=============
+
+All notable changes to this project will be documented in this file.
+
+This project is versioned according to the [Package Versioning Policy](https://pvp.haskell.org), the
+*de facto* standard Haskell versioning scheme.
+
+
+1.1.2.1 [2017-06-07] (tag: [concurrency-1.1.2.1][])
+-------
+
+https://hackage.haskell.org/package/concurrency-1.1.2.1
+
+### Changed
+
+- The `isEmptyMVar` function is now implemented using `tryReadMVar` instead of a combination of
+  `tryTakeMVar` and `putMVar`. It no longer modifies the contents of the `MVar` and can no longer
+  block.
+
+### Miscellaneous
+
+- There is now a changelog.
+
+[concurrency-1.1.2.1]: https://github.com/barrucadu/dejafu/releases/tag/concurrency-1.1.2.1
+
+
+---------------------------------------------------------------------------------------------------
+
+
+1.1.2.0 [2017-04-05] (tag: [concurrency-1.1.2.0][])
+-------
+
+https://hackage.haskell.org/package/concurrency-1.1.2.0
+
+### Control.Concurrent.Classy.Async
+
+- New functions:
+    - `uninterruptibleCancel` function, which is `cancel` inside an
+      uninterruptible mask.
+    - `replicateConcurrently` function, which performs an action many
+      times in separate threads.
+    - `concurrently_`, `mapConcurrently_`, `forConcurrently_`, and
+      `replicateConcurrently_` functions, which discard the result of
+      the non-_ version.
+- New instances:
+    - `Semigroup` instance for `Concurrently` when built with base 4.9.
+    - `Monoid` instance for `Concurrently`.
+
+### Control.Monad.Conc.Class
+
+- The `mask_` and `uninterruptibleMask_` functions from Control.Monad.Catch are now re-exported.
+
+### Changed
+
+- The `cancel` and the `withAsync` functions now block until the `Async` action terminates, to match
+  changes in the main async package.
+
+### Miscellaneous
+
+- Every definition, class, and instance now has a Haddock "@since" annotation.
+
+[concurrency-1.1.2.0]: https://github.com/barrucadu/dejafu/releases/tag/concurrency-1.1.2.0
+
+
+---------------------------------------------------------------------------------------------------
+
+
+1.1.1.0 [2017-03-04] (git tag: [concurrency-1.1.1.0][])
+-------
+
+https://hackage.haskell.org/package/concurrency-1.1.1.0
+
+### Miscellaneous
+
+- The async-dejafu package has been pulled into this package as the Control.Concurrent.Classy.Async
+  module. async-dejafu is now __deprecated__.
+
+[concurrency-1.1.1.0]: https://github.com/barrucadu/dejafu/releases/tag/concurrency-1.1.1.0
+
+
+---------------------------------------------------------------------------------------------------
+
+
+1.1.0.0 [2017-02-21] (git tag: [concurrency-1.1.0.0][])
+-------
+
+https://hackage.haskell.org/package/concurrency-1.1.0.0
+
+### Control.Monad.Conc.Class
+
+- The `MonadConc` class now defines `tryReadMVar`, a non-blocking version of `readMVar` akin to
+  `tryTakeMVar`.
+- The `MonadConc` class no longer defines `_concMessage`, there is no alternative provided, it is
+  just gone.
+
+[concurrency-1.1.0.0]: https://github.com/barrucadu/dejafu/releases/tag/concurrency-1.1.0.0
+
+
+---------------------------------------------------------------------------------------------------
+
+
+1.0.0.0 [2016-09-10] (git tag: [concurrency-1.0.0.0][])
+-------
+
+https://hackage.haskell.org/package/concurrency-1.0.0.0
+
+Initial release. Go read the API docs.
+
+[concurrency-1.0.0.0]: https://github.com/barrucadu/dejafu/releases/tag/concurrency-1.0.0.0
diff --git a/Control/Concurrent/Classy.hs b/Control/Concurrent/Classy.hs
--- a/Control/Concurrent/Classy.hs
+++ b/Control/Concurrent/Classy.hs
@@ -29,12 +29,10 @@
   , module Control.Concurrent.Classy.QSemN
   ) where
 
-import Control.Monad.Conc.Class
-import Control.Concurrent.Classy.Chan
-import Control.Concurrent.Classy.CRef
-import Control.Concurrent.Classy.MVar
-import Control.Concurrent.Classy.STM
-import Control.Concurrent.Classy.QSem
-import Control.Concurrent.Classy.QSemN
-
-{-# ANN module ("HLint: ignore Use import/export shortcut" :: String) #-}
+import           Control.Concurrent.Classy.Chan
+import           Control.Concurrent.Classy.CRef
+import           Control.Concurrent.Classy.MVar
+import           Control.Concurrent.Classy.QSem
+import           Control.Concurrent.Classy.QSemN
+import           Control.Concurrent.Classy.STM
+import           Control.Monad.Conc.Class
diff --git a/Control/Concurrent/Classy/Async.hs b/Control/Concurrent/Classy/Async.hs
--- a/Control/Concurrent/Classy/Async.hs
+++ b/Control/Concurrent/Classy/Async.hs
@@ -77,18 +77,21 @@
   , Concurrently(..)
   ) where
 
-import Control.Applicative
-import Control.Concurrent.Classy.STM.TMVar (newEmptyTMVar, putTMVar, readTMVar)
-import Control.Exception (AsyncException(ThreadKilled), BlockedIndefinitelyOnSTM(..), Exception, SomeException)
-import Control.Monad
-import Control.Monad.Catch (finally, try, onException)
-import Control.Monad.Conc.Class
-import Control.Monad.STM.Class
-import Data.Foldable (foldMap)
-import Data.Traversable
+import           Control.Applicative
+import           Control.Concurrent.Classy.STM.TMVar (newEmptyTMVar, putTMVar,
+                                                      readTMVar)
+import           Control.Exception                   (AsyncException(ThreadKilled),
+                                                      BlockedIndefinitelyOnSTM(..),
+                                                      Exception, SomeException)
+import           Control.Monad
+import           Control.Monad.Catch                 (finally, onException, try)
+import           Control.Monad.Conc.Class
+import           Control.Monad.STM.Class
+import           Data.Foldable                       (foldMap)
+import           Data.Traversable
 
 #if MIN_VERSION_base(4,9,0)
-import Data.Semigroup (Semigroup(..))
+import           Data.Semigroup                      (Semigroup(..))
 #endif
 
 -----------------------------------------------------------------------------------------
@@ -142,7 +145,7 @@
 
 -- | @since 1.1.1.0
 instance MonadConc m => Applicative (Concurrently m) where
-  pure = Concurrently . return
+  pure = Concurrently . pure
 
   Concurrently fs <*> Concurrently as =
     Concurrently $ (\(f, a) -> f a) <$> concurrently fs as
@@ -199,8 +202,7 @@
 asyncUsing doFork action = do
   var <- atomically newEmptyTMVar
   tid <- mask $ \restore -> doFork $ try (restore action) >>= atomically . putTMVar var
-
-  return $ Async tid (readTMVar var)
+  pure (Async tid (readTMVar var))
 
 -- | Fork a thread with the given forking function and give it an
 -- action to unmask exceptions
@@ -208,8 +210,7 @@
 asyncUnmaskUsing doFork action = do
   var <- atomically newEmptyTMVar
   tid <- doFork $ \restore -> try (action restore) >>= atomically . putTMVar var
-
-  return $ Async tid (readTMVar var)
+  pure (Async tid (readTMVar var))
 
 -- | Spawn an asynchronous action in a separate thread, and pass its
 -- @Async@ handle to the supplied function. When the function returns
@@ -245,36 +246,30 @@
 withAsyncOnWithUnmask :: MonadConc m => Int -> ((forall x. m x -> m x) -> m a) -> (Async m a -> m b) -> m b
 withAsyncOnWithUnmask i = withAsyncUnmaskUsing (forkOnWithUnmask i)
 
--- | Fork a thread with the given forking function and kill it when
--- the inner action completes.
---
--- The 'bracket' version appears to hang, even with just IO stuff and
--- using the normal async package. Curious.
+-- | Helper for 'withAsync' and 'withAsyncOn': fork a thread with the
+-- given forking function and kill it when the inner action completes.
 withAsyncUsing :: MonadConc m => (m () -> m (ThreadId m)) -> m a -> (Async m a -> m b) -> m b
 withAsyncUsing doFork action inner = do
   var <- atomically newEmptyTMVar
   tid <- mask $ \restore -> doFork $ try (restore action) >>= atomically . putTMVar var
-
-  let a = Async tid (readTMVar var)
-
-  res <- inner a `catchAll` (\e -> uninterruptibleCancel a >> throw e)
-  cancel a
-
-  return res
+  withAsyncDo (Async tid (readTMVar var)) inner
 
--- | Fork a thread with the given forking function, give it an action
--- to unmask exceptions, and kill it when the inner action completed.
+-- | Helper for 'withAsyncWithUnmask' and 'withAsyncOnWithUnmask':
+-- fork a thread with the given forking function, give it an action to
+-- unmask exceptions, and kill it when the inner action completed.
 withAsyncUnmaskUsing :: MonadConc m => (((forall x. m x -> m x) -> m ()) -> m (ThreadId m)) -> ((forall x. m x -> m x) -> m a) -> (Async m a -> m b) -> m b
 withAsyncUnmaskUsing doFork action inner = do
   var <- atomically newEmptyTMVar
   tid <- doFork $ \restore -> try (action restore) >>= atomically . putTMVar var
-
-  let a = Async tid (readTMVar var)
+  withAsyncDo (Async tid (readTMVar var)) inner
 
+-- | Helper for 'withAsyncUsing' and 'withAsyncUnmaskUsing': run the
+-- inner action and kill the async thread when done.
+withAsyncDo :: MonadConc m => Async m a -> (Async m a -> m b) -> m b
+withAsyncDo a inner = do
   res <- inner a `catchAll` (\e -> uninterruptibleCancel a >> throw e)
   cancel a
-
-  return res
+  pure res
 
 catchAll :: MonadConc m => m a -> (SomeException -> m a) -> m a
 catchAll = catch
@@ -298,7 +293,7 @@
 waitSTM :: MonadConc m => Async m a -> STM m a
 waitSTM a = do
  r <- waitCatchSTM a
- either throwSTM return r
+ either throwSTM pure r
 
 -- | Check whether an 'Async' has completed yet. If it has not
 -- completed yet, then the result is @Nothing@, otherwise the result
@@ -315,7 +310,7 @@
 --
 -- @since 1.1.1.0
 pollSTM :: MonadConc m => Async m a -> STM m (Maybe (Either SomeException a))
-pollSTM (Async _ w) = (Just <$> w) `orElse` return Nothing
+pollSTM (Async _ w) = (Just <$> w) `orElse` pure Nothing
 
 -- | Wait for an asynchronous action to complete, and return either
 -- @Left e@ if the action raised an exception @e@, or @Right a@ if it
@@ -391,7 +386,7 @@
 --
 -- @since 1.1.1.0
 waitAnySTM :: MonadConc m => [Async m a] -> STM m (Async m a, a)
-waitAnySTM = foldr (orElse . (\a -> do r <- waitSTM a; return (a, r))) retry
+waitAnySTM = foldr (orElse . (\a -> do r <- waitSTM a; pure (a, r))) retry
 
 -- | Wait for any of the supplied asynchronous operations to complete.
 -- The value returned is a pair of the 'Async' that completed, and the
@@ -409,7 +404,7 @@
 --
 -- @since 1.1.1.0
 waitAnyCatchSTM :: MonadConc m => [Async m a] -> STM m (Async m a, Either SomeException a)
-waitAnyCatchSTM = foldr (orElse . (\a -> do r <- waitCatchSTM a; return (a, r))) retry
+waitAnyCatchSTM = foldr (orElse . (\a -> do r <- waitCatchSTM a; pure (a, r))) retry
 
 -- | Like 'waitAny', but also cancels the other asynchronous
 -- operations as soon as one has completed.
@@ -503,7 +498,7 @@
 waitBothSTM left right = do
   a <- waitSTM left `orElse` (waitSTM right >> retry)
   b <- waitSTM right
-  return (a, b)
+  pure (a, b)
 
 
 -------------------------------------------------------------------------------
@@ -521,7 +516,7 @@
     r <- atomically w
     case r of
       Left e -> throwTo me e
-      _ -> return ()
+      _ -> pure ()
 
 -- | Link two @Async@s together, such that if either raises an
 -- exception, the same exception is re-thrown in the other @Async@.
@@ -534,7 +529,7 @@
     case r of
       Left  (Left e) -> throwTo tr e
       Right (Left e) -> throwTo tl e
-      _ -> return ()
+      _ -> pure ()
 
 -- | Fork a thread that runs the supplied action, and if it raises an
 -- exception, re-runs the action.  The thread terminates only when the
@@ -545,7 +540,7 @@
         r <- (try :: MonadConc m => m a -> m (Either SomeException a)) $ restore action
         case r of
           Left _ -> go
-          _      -> return ()
+          _      -> pure ()
   in fork go
 
 
@@ -567,7 +562,7 @@
     e <- takeMVar m
     case e of
       Left ex -> throw ex
-      Right r -> return r
+      Right r -> pure r
 
 -- | Like 'race', but the result is ignored.
 --
@@ -593,8 +588,8 @@
 -- @since 1.1.1.0
 concurrently :: MonadConc m => m a -> m b -> m (a, b)
 concurrently left right = concurrently' left right (collect []) where
-  collect [Left a, Right b] _ = return (a, b)
-  collect [Right b, Left a] _ = return (a, b)
+  collect [Left a, Right b] _ = pure (a, b)
+  collect [Right b, Left a] _ = pure (a, b)
   collect xs m = do
     e <- takeMVar m
     case e of
@@ -633,7 +628,7 @@
 
     stop
 
-    return r
+    pure r
 
 -- | Maps a @MonadConc@-performing function over any @Traversable@
 -- data type, performing all the @MonadConc@ actions concurrently, and
diff --git a/Control/Concurrent/Classy/CRef.hs b/Control/Concurrent/Classy/CRef.hs
--- a/Control/Concurrent/Classy/CRef.hs
+++ b/Control/Concurrent/Classy/CRef.hs
@@ -80,7 +80,7 @@
   -- memory barrier.
   ) where
 
-import Control.Monad.Conc.Class
+import           Control.Monad.Conc.Class
 
 -- | Mutate the contents of a @CRef@.
 --
diff --git a/Control/Concurrent/Classy/Chan.hs b/Control/Concurrent/Classy/Chan.hs
--- a/Control/Concurrent/Classy/Chan.hs
+++ b/Control/Concurrent/Classy/Chan.hs
@@ -27,9 +27,9 @@
   , writeList2Chan
   ) where
 
-import Control.Concurrent.Classy.MVar
-import Control.Monad.Catch (mask_)
-import Control.Monad.Conc.Class (MonadConc)
+import           Control.Concurrent.Classy.MVar
+import           Control.Monad.Catch            (mask_)
+import           Control.Monad.Conc.Class       (MonadConc)
 
 -- | 'Chan' is an abstract type representing an unbounded FIFO
 -- channel.
diff --git a/Control/Concurrent/Classy/MVar.hs b/Control/Concurrent/Classy/MVar.hs
--- a/Control/Concurrent/Classy/MVar.hs
+++ b/Control/Concurrent/Classy/MVar.hs
@@ -48,8 +48,9 @@
  , modifyMVarMasked
  ) where
 
-import Control.Monad.Catch (mask_, onException)
-import Control.Monad.Conc.Class
+import           Control.Monad.Catch      (mask_, onException)
+import           Control.Monad.Conc.Class
+import           Data.Maybe               (isJust)
 
 -- | Swap the contents of a @MVar@, and return the value taken. This
 -- function is atomic only if there are no other producers fro this
@@ -60,17 +61,18 @@
 swapMVar cvar a = mask_ $ do
   old <- takeMVar cvar
   putMVar cvar a
-  return old
+  pure old
 
 -- | Check if a @MVar@ is empty.
 --
+-- The boolean value returned is just a snapshot of the state of the
+-- @MVar@, it may have been emptied (or filled) by the time you
+-- actually access it. Generally prefer 'tryPutMVar', 'tryTakeMVar',
+-- and 'tryReadMVar'.
+--
 -- @since 1.0.0.0
 isEmptyMVar :: MonadConc m => MVar m a -> m Bool
-isEmptyMVar cvar = do
-  val <- tryTakeMVar cvar
-  case val of
-    Just val' -> putMVar cvar val' >> return True
-    Nothing   -> return False
+isEmptyMVar = fmap isJust . tryReadMVar
 
 -- | Operate on the contents of a @MVar@, replacing the contents after
 -- finishing. This operation is exception-safe: it will replace the
@@ -85,7 +87,7 @@
   out <- restore (f val) `onException` putMVar cvar val
   putMVar cvar val
 
-  return out
+  pure out
 
 -- | Like 'withMVar', but the @IO@ action in the second argument is
 -- executed with asynchronous exceptions masked.
@@ -98,7 +100,7 @@
   out <- f val `onException` putMVar cvar val
   putMVar cvar val
 
-  return out
+  pure out
 
 -- | An exception-safe wrapper for modifying the contents of a @MVar@.
 -- Like 'withMVar', 'modifyMVar' will replace the original contents of
@@ -121,7 +123,7 @@
   val <- takeMVar cvar
   (val', out) <- restore (f val) `onException` putMVar cvar val
   putMVar cvar val'
-  return out
+  pure out
 
 -- | Like 'modifyMVar_', but the @IO@ action in the second argument is
 -- executed with asynchronous exceptions masked.
@@ -141,4 +143,4 @@
   val <- takeMVar cvar
   (val', out) <- f val `onException` putMVar cvar val
   putMVar cvar val'
-  return out
+  pure out
diff --git a/Control/Concurrent/Classy/QSem.hs b/Control/Concurrent/Classy/QSem.hs
--- a/Control/Concurrent/Classy/QSem.hs
+++ b/Control/Concurrent/Classy/QSem.hs
@@ -15,8 +15,8 @@
   , signalQSem
   ) where
 
-import Control.Concurrent.Classy.QSemN
-import Control.Monad.Conc.Class (MonadConc)
+import           Control.Concurrent.Classy.QSemN
+import           Control.Monad.Conc.Class        (MonadConc)
 
 -- | @QSem@ is a quantity semaphore in which the resource is acquired
 -- and released in units of one. It provides guaranteed FIFO ordering
diff --git a/Control/Concurrent/Classy/QSemN.hs b/Control/Concurrent/Classy/QSemN.hs
--- a/Control/Concurrent/Classy/QSemN.hs
+++ b/Control/Concurrent/Classy/QSemN.hs
@@ -16,10 +16,11 @@
   , signalQSemN
   ) where
 
-import Control.Monad.Conc.Class (MonadConc)
-import Control.Concurrent.Classy.MVar
-import Control.Monad.Catch (mask_, onException, uninterruptibleMask_)
-import Data.Maybe
+import           Control.Concurrent.Classy.MVar
+import           Control.Monad.Catch            (mask_, onException,
+                                                 uninterruptibleMask_)
+import           Control.Monad.Conc.Class       (MonadConc)
+import           Data.Maybe
 
 -- | 'QSemN' is a quantity semaphore in which the resource is aqcuired
 -- and released in units of one. It provides guaranteed FIFO ordering
diff --git a/Control/Concurrent/Classy/STM.hs b/Control/Concurrent/Classy/STM.hs
--- a/Control/Concurrent/Classy/STM.hs
+++ b/Control/Concurrent/Classy/STM.hs
@@ -17,12 +17,10 @@
   , module Control.Concurrent.Classy.STM.TArray
   ) where
 
-import Control.Monad.STM.Class
-import Control.Concurrent.Classy.STM.TVar
-import Control.Concurrent.Classy.STM.TMVar
-import Control.Concurrent.Classy.STM.TChan
-import Control.Concurrent.Classy.STM.TQueue
-import Control.Concurrent.Classy.STM.TBQueue
-import Control.Concurrent.Classy.STM.TArray
-
-{-# ANN module ("HLint: ignore Use import/export shortcut" :: String) #-}
+import           Control.Concurrent.Classy.STM.TArray
+import           Control.Concurrent.Classy.STM.TBQueue
+import           Control.Concurrent.Classy.STM.TChan
+import           Control.Concurrent.Classy.STM.TMVar
+import           Control.Concurrent.Classy.STM.TQueue
+import           Control.Concurrent.Classy.STM.TVar
+import           Control.Monad.STM.Class
diff --git a/Control/Concurrent/Classy/STM/TArray.hs b/Control/Concurrent/Classy/STM/TArray.hs
--- a/Control/Concurrent/Classy/STM/TArray.hs
+++ b/Control/Concurrent/Classy/STM/TArray.hs
@@ -1,4 +1,5 @@
-{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
 
 -- |
 -- Module      : Control.Concurrent.Classy.STM.
@@ -15,12 +16,12 @@
 -- an @Eq@ constraint.
 module Control.Concurrent.Classy.STM.TArray (TArray) where
 
-import Data.Array (Array, bounds)
-import Data.Array.Base (listArray, arrEleBottom, unsafeAt, MArray(..),
-                        IArray(numElements))
-import Data.Ix (rangeSize)
+import           Data.Array              (Array, bounds)
+import           Data.Array.Base         (IArray(numElements), MArray(..),
+                                          arrEleBottom, listArray, unsafeAt)
+import           Data.Ix                 (rangeSize)
 
-import Control.Monad.STM.Class
+import           Control.Monad.STM.Class
 
 -- | @TArray@ is a transactional array, supporting the usual 'MArray'
 -- interface for mutable arrays.
diff --git a/Control/Concurrent/Classy/STM/TBQueue.hs b/Control/Concurrent/Classy/STM/TBQueue.hs
--- a/Control/Concurrent/Classy/STM/TBQueue.hs
+++ b/Control/Concurrent/Classy/STM/TBQueue.hs
@@ -33,7 +33,7 @@
   , isFullTBQueue
   ) where
 
-import Control.Monad.STM.Class
+import           Control.Monad.STM.Class
 
 -- | 'TBQueue' is an abstract type representing a bounded FIFO
 -- channel.
@@ -113,7 +113,7 @@
 peekTBQueue c = do
   x <- readTBQueue c
   unGetTBQueue c x
-  return x
+  pure x
 
 -- | A version of 'peekTBQueue' which does not retry. Instead it
 -- returns @Nothing@ if no value is available.
diff --git a/Control/Concurrent/Classy/STM/TChan.hs b/Control/Concurrent/Classy/STM/TChan.hs
--- a/Control/Concurrent/Classy/STM/TChan.hs
+++ b/Control/Concurrent/Classy/STM/TChan.hs
@@ -32,7 +32,7 @@
   , isEmptyTChan
   ) where
 
-import Control.Monad.STM.Class
+import           Control.Monad.STM.Class
 
 -- | 'TChan' is an abstract type representing an unbounded FIFO
 -- channel.
@@ -125,7 +125,7 @@
 dupTChan (TChan _ writeT) = do
   hole   <- readTVar writeT
   readT' <- newTVar hole
-  return (TChan readT' writeT)
+  pure (TChan readT' writeT)
 
 -- | Put a data item back onto a channel, where it will be the next
 -- item read.
diff --git a/Control/Concurrent/Classy/STM/TMVar.hs b/Control/Concurrent/Classy/STM/TMVar.hs
--- a/Control/Concurrent/Classy/STM/TMVar.hs
+++ b/Control/Concurrent/Classy/STM/TMVar.hs
@@ -29,9 +29,9 @@
   , swapTMVar
   ) where
 
-import Control.Monad (liftM, when, unless)
-import Control.Monad.STM.Class
-import Data.Maybe (isJust, isNothing)
+import           Control.Monad           (liftM, unless, when)
+import           Control.Monad.STM.Class
+import           Data.Maybe              (isJust, isNothing)
 
 -- | A @TMVar@ is like an @MVar@ or a @mVar@, but using transactional
 -- memory. As transactions are atomic, this makes dealing with
@@ -57,7 +57,7 @@
 newTMVarN n a = do
   let n' = if null n then "ctmvar" else "ctmvar-" ++ n
   ctvar <- newTVarN n' $ Just a
-  return $ TMVar ctvar
+  pure (TMVar ctvar)
 
 -- | Create a new empty 'TMVar'.
 --
@@ -75,7 +75,7 @@
 newEmptyTMVarN n = do
   let n' = if null n then "ctmvar" else "ctmvar-" ++ n
   ctvar <- newTVarN n' Nothing
-  return $ TMVar ctvar
+  pure (TMVar ctvar)
 
 -- | Take the contents of a 'TMVar', or 'retry' if it is empty.
 --
@@ -83,7 +83,7 @@
 takeTMVar :: MonadSTM stm => TMVar stm a -> stm a
 takeTMVar ctmvar = do
   taken <- tryTakeTMVar ctmvar
-  maybe retry return taken
+  maybe retry pure taken
 
 -- | Write to a 'TMVar', or 'retry' if it is full.
 --
@@ -99,7 +99,7 @@
 readTMVar :: MonadSTM stm => TMVar stm a -> stm a
 readTMVar ctmvar = do
   readed <- tryReadTMVar ctmvar
-  maybe retry return readed
+  maybe retry pure readed
 
 -- | Try to take the contents of a 'TMVar', returning 'Nothing' if it
 -- is empty.
@@ -109,7 +109,7 @@
 tryTakeTMVar (TMVar ctvar) = do
   val <- readTVar ctvar
   when (isJust val) $ writeTVar ctvar Nothing
-  return val
+  pure val
 
 -- | Try to write to a 'TMVar', returning 'False' if it is full.
 --
@@ -118,7 +118,7 @@
 tryPutTMVar (TMVar ctvar) a = do
   val <- readTVar ctvar
   when (isNothing val) $ writeTVar ctvar (Just a)
-  return $ isNothing val
+  pure (isNothing val)
 
 -- | Try to read from a 'TMVar' without emptying, returning 'Nothing'
 -- if it is empty.
@@ -141,4 +141,4 @@
 swapTMVar ctmvar a = do
   val <- takeTMVar ctmvar
   putTMVar ctmvar a
-  return val
+  pure val
diff --git a/Control/Concurrent/Classy/STM/TQueue.hs b/Control/Concurrent/Classy/STM/TQueue.hs
--- a/Control/Concurrent/Classy/STM/TQueue.hs
+++ b/Control/Concurrent/Classy/STM/TQueue.hs
@@ -36,7 +36,7 @@
   , isEmptyTQueue
   ) where
 
-import Control.Monad.STM.Class
+import           Control.Monad.STM.Class
 
 -- | 'TQueue' is an abstract type representing an unbounded FIFO channel.
 --
diff --git a/Control/Concurrent/Classy/STM/TVar.hs b/Control/Concurrent/Classy/STM/TVar.hs
--- a/Control/Concurrent/Classy/STM/TVar.hs
+++ b/Control/Concurrent/Classy/STM/TVar.hs
@@ -25,9 +25,9 @@
   , registerDelay
   ) where
 
-import Control.Monad.STM.Class
-import Control.Monad.Conc.Class
-import Data.Functor (void)
+import           Control.Monad.Conc.Class
+import           Control.Monad.STM.Class
+import           Data.Functor             (void)
 
 -- * @TVar@s
 
@@ -54,7 +54,7 @@
 swapTVar ctvar a = do
   old <- readTVar ctvar
   writeTVar ctvar a
-  return old
+  pure old
 
 -- | Set the value of returned 'TVar' to @True@ after a given number
 -- of microseconds. The caveats associated with 'threadDelay' also
diff --git a/Control/Monad/Conc/Class.hs b/Control/Monad/Conc/Class.hs
--- a/Control/Monad/Conc/Class.hs
+++ b/Control/Monad/Conc/Class.hs
@@ -66,32 +66,32 @@
   ) where
 
 -- for the class and utilities
-import Control.Exception (Exception, AsyncException(ThreadKilled), SomeException)
-import Control.Monad.Catch (MonadCatch, MonadThrow, MonadMask)
-import qualified Control.Monad.Catch as Ca
-import Control.Monad.STM.Class (MonadSTM, TVar, readTVar)
-import Control.Monad.Trans.Control (MonadTransControl, StT, liftWith)
-import Data.Proxy (Proxy(..))
+import           Control.Exception            (AsyncException(ThreadKilled),
+                                               Exception, SomeException)
+import           Control.Monad.Catch          (MonadCatch, MonadMask,
+                                               MonadThrow)
+import qualified Control.Monad.Catch          as Ca
+import           Control.Monad.STM.Class      (MonadSTM, TVar, readTVar)
+import           Control.Monad.Trans.Control  (MonadTransControl, StT, liftWith)
+import           Data.Proxy                   (Proxy(..))
 
 -- for the 'IO' instance
-import qualified Control.Concurrent as IO
-import qualified Control.Concurrent.STM.TVar as IO
-import qualified Control.Monad.STM as IO
-import qualified Data.Atomics as IO
-import qualified Data.IORef as IO
+import qualified Control.Concurrent           as IO
+import qualified Control.Concurrent.STM.TVar  as IO
+import qualified Control.Monad.STM            as IO
+import qualified Data.Atomics                 as IO
+import qualified Data.IORef                   as IO
 
 -- for the transformer instances
-import Control.Monad.Reader (ReaderT)
-import Control.Monad.Trans (lift)
-import Control.Monad.Trans.Identity (IdentityT)
-import qualified Control.Monad.RWS.Lazy as RL
-import qualified Control.Monad.RWS.Strict as RS
-import qualified Control.Monad.State.Lazy as SL
-import qualified Control.Monad.State.Strict as SS
-import qualified Control.Monad.Writer.Lazy as WL
-import qualified Control.Monad.Writer.Strict as WS
-
-{-# ANN module ("HLint: ignore Use const" :: String) #-}
+import           Control.Monad.Reader         (ReaderT)
+import qualified Control.Monad.RWS.Lazy       as RL
+import qualified Control.Monad.RWS.Strict     as RS
+import qualified Control.Monad.State.Lazy     as SL
+import qualified Control.Monad.State.Strict   as SS
+import           Control.Monad.Trans          (lift)
+import           Control.Monad.Trans.Identity (IdentityT)
+import qualified Control.Monad.Writer.Lazy    as WL
+import qualified Control.Monad.Writer.Strict  as WS
 
 -- | @MonadConc@ is an abstraction over GHC's typical concurrency
 -- abstraction. It captures the interface of concurrency monads in
@@ -167,11 +167,11 @@
   -- | Fork a computation to happen concurrently. Communication may
   -- happen over @MVar@s.
   --
-  -- > fork ma = forkWithUnmask (\_ -> ma)
+  -- > fork ma = forkWithUnmask (const ma)
   --
   -- @since 1.0.0.0
   fork :: m () -> m (ThreadId m)
-  fork ma = forkWithUnmask (\_ -> ma)
+  fork ma = forkWithUnmask (const ma)
 
   -- | Like 'fork', but the child thread is passed a function that can
   -- be used to unmask asynchronous exceptions. This function should
@@ -202,11 +202,11 @@
   -- implementation dependent. The int is interpreted modulo to the
   -- total number of capabilities as returned by 'getNumCapabilities'.
   --
-  -- > forkOn c ma = forkOnWithUnmask c (\_ -> ma)
+  -- > forkOn c ma = forkOnWithUnmask c (const ma)
   --
   -- @since 1.0.0.0
   forkOn :: Int -> m () -> m (ThreadId m)
-  forkOn c ma = forkOnWithUnmask c (\_ -> ma)
+  forkOn c ma = forkOnWithUnmask c (const ma)
 
   -- | Like 'forkWithUnmask', but the child thread is pinned to the
   -- given CPU, as with 'forkOn'.
@@ -477,7 +477,7 @@
 --
 -- @since 1.0.0.0
 forkN :: MonadConc m => String -> m () -> m (ThreadId m)
-forkN name ma = forkWithUnmaskN name (\_ -> ma)
+forkN name ma = forkWithUnmaskN name (const ma)
 
 -- | Like 'forkOn', but the thread is given a name which may be used
 -- to present more useful debugging information.
@@ -488,7 +488,7 @@
 --
 -- @since 1.0.0.0
 forkOnN :: MonadConc m => String -> Int -> m () -> m (ThreadId m)
-forkOnN name i ma = forkOnWithUnmaskN name i (\_ -> ma)
+forkOnN name i ma = forkOnWithUnmaskN name i (const ma)
 
 -- Bound Threads
 
diff --git a/Control/Monad/STM/Class.hs b/Control/Monad/STM/Class.hs
--- a/Control/Monad/STM/Class.hs
+++ b/Control/Monad/STM/Class.hs
@@ -31,21 +31,21 @@
   , liftedOrElse
   ) where
 
-import Control.Exception (Exception)
-import Control.Monad (unless)
-import Control.Monad.Reader (ReaderT)
-import Control.Monad.Trans (lift)
-import Control.Monad.Trans.Control (MonadTransControl, StT, liftWith)
-import Control.Monad.Trans.Identity (IdentityT)
+import           Control.Exception            (Exception)
+import           Control.Monad                (unless)
+import           Control.Monad.Reader         (ReaderT)
+import           Control.Monad.Trans          (lift)
+import           Control.Monad.Trans.Control  (MonadTransControl, StT, liftWith)
+import           Control.Monad.Trans.Identity (IdentityT)
 
-import qualified Control.Concurrent.STM as STM
-import qualified Control.Monad.Catch as Ca
-import qualified Control.Monad.RWS.Lazy as RL
-import qualified Control.Monad.RWS.Strict as RS
-import qualified Control.Monad.State.Lazy as SL
-import qualified Control.Monad.State.Strict as SS
-import qualified Control.Monad.Writer.Lazy as WL
-import qualified Control.Monad.Writer.Strict as WS
+import qualified Control.Concurrent.STM       as STM
+import qualified Control.Monad.Catch          as Ca
+import qualified Control.Monad.RWS.Lazy       as RL
+import qualified Control.Monad.RWS.Strict     as RS
+import qualified Control.Monad.State.Lazy     as SL
+import qualified Control.Monad.State.Strict   as SS
+import qualified Control.Monad.Writer.Lazy    as WL
+import qualified Control.Monad.Writer.Strict  as WS
 
 -- | @MonadSTM@ is an abstraction over 'STM'.
 --
diff --git a/README.markdown b/README.markdown
new file mode 100644
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,67 @@
+concurrency
+===========
+
+A typeclass abstraction over much of Control.Concurrent (and some
+extras!). If you're looking for a general introduction to Haskell
+concurrency, you should check out the excellent
+[Parallel and Concurrent Programming in Haskell][parconc], by Simon
+Marlow. If you are already familiar with concurrent Haskell, just
+change all the imports from Control.Concurrent.* to
+Control.Concurrent.Classy.* and fix the type errors.
+
+A brief list of supported functionality:
+
+- Threads: the `forkIO*` and `forkOn*` functions, although bound
+  threads are not supported.
+- Getting and setting capablities.
+- Yielding and delaying.
+- Mutable state: STM, `MVar`, and `IORef`.
+- Atomic compare-and-swap for `IORef`.
+- Exceptions.
+- All of the data structures in Control.Concurrent.* and
+  Control.Concurrent.STM.* have typeclass-abstracted equivalents.
+- A reimplementation of the [async][] package, providing a
+  higher-level interface over threads, allowing users to conveniently
+  run `MonadConc` operations asynchronously and wait for their
+  results.
+
+This is quite a rich set of functionality, although it is not
+complete. If there is something else you need, file an issue!
+
+This used to be part of dejafu, but with the dejafu-0.4.0.0 release,
+it was split out into its own package.
+
+The documentation of the latest developmental version is
+[available online][docs].
+
+Why this and not something else?
+--------------------------------
+
+- **Why not base:** like lifted-base, concurrency uses typeclasses to
+  make function types more generic. This automatically eliminates
+  calls to `lift` in many cases, resulting in clearer and simpler
+  code.
+
+- **Why not lifted-base:** fundamentally, lifted-base is still using
+  actual threads and actual mutable variables. When using a
+  concurrency-specific typeclass, this isn't necessarily the case. The
+  dejafu library provides non-IO-based implementations to allow
+  testing concurrent programs.
+
+- **Why not IOSpec:** IOSpec provides many of the operations this
+  library does, however it uses a free monad to do so, which has extra
+  allocation overhead. Furthermore, it does not expose enough of the
+  internals in order to accurately test real-execution semantics, such
+  as relaxed memory.
+
+Contributing
+------------
+
+Bug reports, pull requests, and comments are very welcome!
+
+Feel free to contact me on GitHub, through IRC (#haskell on freenode),
+or email (mike@barrucadu.co.uk).
+
+[docs]:    https://docs.barrucadu.co.uk/concurrency/dejafu-0.4
+[async]:   https://hackage.haskell.org/package/async
+[parconc]: http://chimera.labs.oreilly.com/books/1230000000929
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,2 +1,2 @@
-import Distribution.Simple
+import           Distribution.Simple
 main = defaultMain
diff --git a/concurrency.cabal b/concurrency.cabal
--- a/concurrency.cabal
+++ b/concurrency.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                concurrency
-version:             1.1.2.0
+version:             1.1.2.1
 synopsis:            Typeclasses, functions, and data types for concurrency and STM.
 
 description:
@@ -13,58 +13,6 @@
   already familiar with concurrent Haskell, just change all the
   imports from Control.Concurrent.* to Control.Concurrent.Classy.* and
   fix the type errors.
-  .
-  A brief list of supported functionality:
-  .
-  * Threads: the @forkIO*@ and @forkOn*@ functions, although bound
-    threads are not supported.
-  .
-  * Getting and setting capablities.
-  .
-  * Yielding and delaying.
-  .
-  * Mutable state: STM, @MVar@, and @IORef@.
-  .
-  * Atomic compare-and-swap for @IORef@.
-  .
-  * Exceptions.
-  .
-  * All of the data structures in Control.Concurrent.* and
-    Control.Concurrent.STM.* have typeclass-abstracted equivalents.
-  .
-  * A reimplementation of the
-    <https://hackage.haskell.org/package/async async> package,
-    providing a higher-level interface over threads, allowing users to
-    conveniently run @MonadConc@ operations asynchronously and wait
-    for their results.
-  .
-  This is quite a rich set of functionality, although it is not
-  complete. If there is something else you need, file an issue!
-  .
-  This used to be part of dejafu, but with the dejafu-0.4.0.0 release,
-  it was split out into its own package.
-  .
-  == Why this and not something else?
-  .
-  * Why not base: like lifted-base, concurrency uses typeclasses to
-    make function types more generic. This automatically eliminates
-    calls to `lift` in many cases, resulting in clearer and simpler
-    code.
-  .
-  * Why not lifted-base: fundamentally, lifted-base is still using
-    actual threads and actual mutable variables. When using a
-    concurrency-specific typeclass, this isn't necessarily the case.
-    The dejafu library provides non-IO-based implementations to allow
-    testing concurrent programs.
-  .
-  * Why not IOSpec: IOSpec provides many of the operations this
-    library does, however it uses a free monad to do so, which has
-    extra allocation overhead. Furthermore, it does not expose enough
-    of the internals in order to accurately test real-execution
-    semantics, such as relaxed memory.
-  .
-  See the <https://github.com/barrucadu/dejafu README> for more
-  details.
 
 homepage:            https://github.com/barrucadu/dejafu
 license:             MIT
@@ -74,7 +22,7 @@
 -- copyright:           
 category:            Concurrency
 build-type:          Simple
--- extra-source-files:  
+extra-source-files:  README.markdown CHANGELOG.markdown
 cabal-version:       >=1.10
 
 source-repository head
@@ -84,7 +32,7 @@
 source-repository this
   type:     git
   location: https://github.com/barrucadu/dejafu.git
-  tag:      concurrency-1.1.2.0
+  tag:      concurrency-1.1.2.1
 
 library
   exposed-modules:     Control.Monad.Conc.Class
