diff --git a/Properties.hs b/Properties.hs
new file mode 100644
--- /dev/null
+++ b/Properties.hs
@@ -0,0 +1,16 @@
+import Control.Monad.IO.Class
+import Test.QuickCheck
+import Test.QuickCheck.Monadic
+import qualified Data.RingBuffer as R
+import qualified Data.Vector as V
+
+testAppend :: (Eq a)
+           => Positive Int -> [a] -> Property
+testAppend (Positive cap) xs = monadicIO $ do
+    r <- liftIO $ R.new cap :: PropertyM IO (R.RingBuffer V.Vector a)
+    liftIO $ mapM_ (`R.append` r) (reverse xs)
+    xs' <- liftIO $ R.toList r
+    return $ xs' == take cap xs
+
+main :: IO ()
+main = quickCheck (testAppend :: Positive Int -> [Int] -> Property)
diff --git a/ring-buffer.cabal b/ring-buffer.cabal
--- a/ring-buffer.cabal
+++ b/ring-buffer.cabal
@@ -1,5 +1,5 @@
 name:                ring-buffer
-version:             0.1.3
+version:             0.2.0
 synopsis:            A concurrent, mutable ring-buffer
 description:         A concurrent, mutable ring-buffer
 homepage:            http://github.com/bgamari/ring-buffer
@@ -24,3 +24,12 @@
                        primitive >=0.5 && <0.7
   hs-source-dirs:      src
   default-language:    Haskell2010
+
+test-suite properties
+  type:                exitcode-stdio-1.0
+  main-is:             Properties.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.9,
+                       QuickCheck >= 2.7,
+                       vector >= 0.11,
+                       ring-buffer
diff --git a/src/Data/RingBuffer.hs b/src/Data/RingBuffer.hs
--- a/src/Data/RingBuffer.hs
+++ b/src/Data/RingBuffer.hs
@@ -8,6 +8,8 @@
                        , concat
                        , capacity
                        , length
+                       , latest
+                       , toList
                        , withItems
                        ) where
 
@@ -49,8 +51,11 @@
     put $ RingState (full || a > 0) pos'
 
 -- | Create a new ring of a given length
+--
+-- /Note:/ size must be non-zero
 new :: (VG.Vector v a) => Int -> IO (RingBuffer v a)
 new n = do
+    when (n < 1) $ fail "Data.RingBuffer.new: invalid ring size"
     buffer <- VGM.new n
     state <- newMVar $ RingState False 0
     return $ RingBuffer { ringBuffer=buffer, ringState=state }
@@ -106,6 +111,30 @@
 -- | The current filled length of the ring
 length :: (VG.Vector v a) => RingBuffer v a -> IO Int
 length rb = withRing rb length'
+
+-- | Retrieve the $n$th most-recently added item of the ring
+latest :: (VG.Vector v a) => RingBuffer v a -> Int -> IO (Maybe a)
+latest rb n = withRing rb $ do
+    len <- length'
+    if n >= len
+      then return Nothing
+      else Just <$> latest' n
+
+latest' :: (VGM.MVector v a, MonadIO m) => Int -> RingM m v a a
+latest' n = do
+    len <- length'
+    cap <- capacity'
+    when (n >= len) $ error "Data.RingBuffer.latest': invalid index"
+    let idx = (cap + len - n - 1) `mod` cap
+    buf <- ask
+    liftIO $ VGM.unsafeRead buf idx
+
+-- | Get the entire contents of the ring, with the most recently added element
+-- at the head. Note that this is rather inefficient.
+toList :: (VG.Vector v a) => RingBuffer v a -> IO [a]
+toList rb = withRing rb $ do
+    len <- length'
+    mapM latest' [0..len-1]
 
 -- | Execute the given action with the items of the ring.
 -- Note that no references to the vector may leak out of the action as
