diff --git a/retry.cabal b/retry.cabal
--- a/retry.cabal
+++ b/retry.cabal
@@ -14,7 +14,7 @@
         case we should hang back for a bit and retry the query instead
         of simply raising an exception.
 
-version:             0.4
+version:             0.5
 synopsis:            Retry combinators for monadic actions that may fail
 license:             BSD3
 license-file:        LICENSE
@@ -29,10 +29,31 @@
 library
   exposed-modules:     Control.Retry
   build-depends:       
-    base            ==4.*, 
-    exceptions      >= 0.5 && < 0.6,
-    transformers,
-    data-default
+      base                 ==4.*
+    , data-default-class
+    , exceptions           >= 0.5 && < 0.7
+    , transformers         < 0.5
   hs-source-dirs:      src
   default-language:    Haskell2010
 
+
+test-suite test
+    type:           exitcode-stdio-1.0
+    main-is:        main.hs
+    hs-source-dirs: test,src
+    ghc-options:    -threaded
+    build-depends:       
+        base              ==4.*  
+      , exceptions        >= 0.5   && < 0.6
+      , transformers
+      , data-default-class
+      , time
+      , QuickCheck         >= 2.7 && < 2.8
+      , HUnit              >= 1.2.5.2 && < 1.3
+      , hspec              >= 1.9 && < 1.10
+    default-language: Haskell2010
+
+
+
+
+ 
diff --git a/src/Control/Retry.hs b/src/Control/Retry.hs
--- a/src/Control/Retry.hs
+++ b/src/Control/Retry.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE BangPatterns          #-}
-{-# LANGUAGE DeriveDataTypeable    #-}
 {-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE RankNTypes            #-}
@@ -30,91 +29,122 @@
 module Control.Retry
     (
       -- * High Level Operation
-      RetrySettings (..)
-    , RetryLimit(..)
-    , limitedRetries
-    , unlimitedRetries
+      RetryPolicy (..)
 
     , retrying
     , recovering
     , recoverAll
 
-    -- * Utilities
-    , delay
-    , performDelay
-    , flatDelay
-    , backoffDelay
-    , backoffDelayFor
+    -- * Retry Policies
+    , constantDelay
+    , exponentialBackoff
+    , fibonacciBackoff
+    , limitRetries
+
+    -- * Re-export from Data.Monoid
+
+    , (<>)
+
     ) where
 
 -------------------------------------------------------------------------------
 import           Control.Concurrent
 import           Control.Monad.Catch
 import           Control.Monad.IO.Class
-import           Data.Default
+import           Data.Default.Class
+import           Data.Monoid
 import           Prelude                hiding (catch)
 -------------------------------------------------------------------------------
 
 
-data RetryLimit = RLimit Int
-                | RNoLimit
-
-
--- | Set a limited number of retries. Default in 'def' is 5.
-limitedRetries :: Int -> RetryLimit
-limitedRetries = RLimit
+-------------------------------------------------------------------------------
+-- | A 'RetryPolicy' is a function that takes an iteration number and
+-- possibly returns a delay in miliseconds. *Nothing* implies we have
+-- reached the retry limit.
+--
+-- Please note that 'RetryPolicy' is a 'Monoid'. You can collapse
+-- multiple strategies into one using 'mappend' or '<>'. The semantics
+-- of this combination are as follows:
+--
+-- 1. If either policy returns 'Nothing', the combined policy returns
+-- 'Nothing'. This can be used to @inhibit@ after a number of retries,
+-- for example.
+--
+-- 2. If both policies return a delay, the larger delay will be used.
+-- This is quite natural when combining multiple policies to achieve a
+-- certain effect.
+--
+-- Example:
+--
+-- One can easily define an exponential backoff policy with a limited
+-- number of retries:
+--
+-- >> limitedBackoff = exponentialBackoff 50 <> limitedRetries 5
+--
+-- Naturally, 'mempty' will retry immediately (delay 0) for an
+-- unlimited number of retries, forming the identity for the 'Monoid'.
+--
+-- The default under 'def' implements a constant 50ms delay, up to 5 times:
+--
+-- >> def = constantDelay 50000 <> limitRetries 5
+--
+-- For anything more complex, just define your own 'RetryPolicy':
+--
+-- >> myPolicy = RetryPolicy $ \ n -> if n > 10 then Just 1000 else Just 10000
+newtype RetryPolicy = RetryPolicy { getRetryPolicy :: Int -> Maybe Int }
 
 
--- | Set an unlimited number of retries. Note that with this option
--- turned on, the combinator will keep retrying the action
--- indefinitely and might essentially hang in some cases.
-unlimitedRetries :: RetryLimit
-unlimitedRetries = RNoLimit
-
+instance Default RetryPolicy where
+    def = constantDelay 50000 <> limitRetries 5
 
--- | Settings for retry behavior. Simply using 'def' for default
--- values should work in most cases.
-data RetrySettings = RetrySettings {
-      numRetries :: RetryLimit
-    -- ^ Number of retries. Defaults to 5.
-    , backoff    :: Bool
-    -- ^ Whether to implement exponential backoff in retries. Defaults
-    -- to True.
-    , baseDelay  :: Int
-    -- ^ The base delay in miliseconds. Defaults to 50. Without
-    -- 'backoff', this is the delay. With 'backoff', this base delay
-    -- will grow by a factor of 2 on each subsequent retry.
-    }
+instance Monoid RetryPolicy where
+    mempty = RetryPolicy $ (const (Just 0))
+    (RetryPolicy a) `mappend` (RetryPolicy b) = RetryPolicy $ \ n -> do
+      a' <- a n
+      b' <- b n
+      return $! max a' b'
 
 
-instance Default RetrySettings where
-    def = RetrySettings (limitedRetries 5) True 50
+-------------------------------------------------------------------------------
+-- | Retry immediately, but only up to @n@ times.
+limitRetries
+    :: Int
+    -- ^ Maximum number of retries.
+    -> RetryPolicy
+limitRetries i = RetryPolicy $ \ n -> if n >= i then Nothing else (Just 0)
 
 
--- | Delay thread using backoff delay for the nth retry.
-backoffDelay :: MonadIO m => RetrySettings -> Int -> m ()
-backoffDelay set !n = liftIO . threadDelay $ backoffDelayFor (delay set) n
+-------------------------------------------------------------------------------
+-- | Implement a constant delay with unlimited retries.
+constantDelay
+    :: Int
+    -- ^ Base delay in microseconds
+    -> RetryPolicy
+constantDelay delay = RetryPolicy (const (Just delay))
 
 
--- | Delay for nth iteration of exponential backoff, in microseconds
-backoffDelayFor
+-------------------------------------------------------------------------------
+-- | Grow delay exponentially each iteration.
+exponentialBackoff
     :: Int
     -- ^ Base delay in microseconds
-    -> Int
-    -- ^ Iteration number, starting at 0.
-    -> Int
-backoffDelayFor base n = 2^n * base
+    -> RetryPolicy
+exponentialBackoff base = RetryPolicy $ \ n -> Just (2^n * base)
 
 
--- | Delay thread using flat delay
-flatDelay :: MonadIO m => RetrySettings -> t -> m ()
-flatDelay set@RetrySettings{..} !_ = liftIO (threadDelay $ delay set)
-
--- | Delay in micro seconds
-delay :: RetrySettings -> Int
-delay RetrySettings{..} = baseDelay * 1000
+-------------------------------------------------------------------------------
+-- | Implement Fibonacci backoff.
+fibonacciBackoff
+    :: Int
+    -- ^ Base delay in microseconds
+    -> RetryPolicy
+fibonacciBackoff base = RetryPolicy $ \ n -> Just $ fib (n + 1) (0, base)
+    where
+      fib 0 (a, _) = a
+      fib !m (!a, !b) = fib (m-1) (b, a + b)
 
 
+-------------------------------------------------------------------------------
 -- | Retry combinator for actions that don't raise exceptions, but
 -- signal in their type the outcome has failed. Examples are the
 -- 'Maybe', 'Either' and 'EitherT' monads.
@@ -136,30 +166,30 @@
 -- Note how the latest failing result is returned after all retries
 -- have been exhausted.
 retrying :: MonadIO m
-         => RetrySettings
-         -> (b -> Bool)
-         -- ^ A function to check whether the result should be
-         -- retried. If True, we delay and retry the operation.
+         => RetryPolicy
+         -> (Int -> b -> m Bool)
+         -- ^ An action to check whether the result should be retried.
+         -- If True, we delay and retry the operation.
          -> m b
          -- ^ Action to run
          -> m b
-retrying set@RetrySettings{..} chk f = go 0
+retrying (RetryPolicy policy) chk f = go 0
     where
-      retry n = do
-          performDelay set n
-          go $! n+1
-
       go n = do
           res <- f
-          case chk res of
+          chk' <- chk n res
+          case chk' of
             True ->
-              case numRetries of
-                RNoLimit -> retry n
-                RLimit lim -> if n >= lim then return res else retry n
+              case (policy n) of
+                Just delay -> do
+                  liftIO (threadDelay delay)
+                  go $! n+1
+                Nothing -> return res
             False -> return res
 
 
 
+-------------------------------------------------------------------------------
 -- | Retry ALL exceptions that may be raised. To be used with caution;
 -- this matches the exception on 'SomeException'.
 --
@@ -176,40 +206,29 @@
 -- Running action
 -- *** Exception: this is an error
 recoverAll :: (MonadIO m, MonadCatch m)
-         => RetrySettings
+         => RetryPolicy
          -> m a
          -> m a
 recoverAll set f = recovering set [h] f
     where
-      h = Handler $ \ (_ :: SomeException) -> return True
-
-
--- | Perform 'threadDelay' for the nth retry for the given settings.
-performDelay :: MonadIO m => RetrySettings -> Int -> m ()
-performDelay set@RetrySettings{..} n =
-    if backoff
-      then backoffDelay set n
-      else flatDelay set n
+      h _ = Handler $ \ (_ :: SomeException) -> return True
 
 
+-------------------------------------------------------------------------------
 -- | Run an action and recover from a raised exception by potentially
 -- retrying the action a number of times.
 recovering :: forall m a. (MonadIO m, MonadCatch m)
-           => RetrySettings
+           => RetryPolicy
            -- ^ Just use 'def' faor default settings
-           -> [Handler m Bool]
+           -> [(Int -> Handler m Bool)]
            -- ^ Should a given exception be retried? Action will be
            -- retried if this returns True.
            -> m a
            -- ^ Action to perform
            -> m a
-recovering set@RetrySettings{..} hs f = go 0
+recovering (RetryPolicy policy) hs f = go 0
     where
-      retry n = do
-          performDelay set n
-          go $! n+1
 
-
       -- | Convert a (e -> m Bool) handler into (e -> m a) so it can
       -- be wired into the 'catches' combinator.
       transHandler :: Int -> Handler m Bool -> Handler m a
@@ -217,15 +236,18 @@
           chk <- h e
           case chk of
             True ->
-              case numRetries of
-                RNoLimit -> retry n
-                RLimit lim -> if n >= lim then throwM e else retry n
+              case policy n of
+                Just delay -> do
+                  liftIO (threadDelay delay)
+                  go $! n+1
+                Nothing -> throwM e
             False -> throwM e
 
-      go n = f `catches` map (transHandler n) hs
+      go n = f `catches` map (transHandler n . ($ n)) hs
 
 
 
+
                               ------------------
                               -- Simple Tests --
                               ------------------
@@ -241,6 +263,6 @@
 
 -- test = retrying def [h1,h2] f
 --     where
---       f = putStrLn "Running action" >> throw AnotherException
+--       f = putStrLn "Running action" >> throwM AnotherException
 --       h1 = Handler $ \ (e :: TestException) -> return False
 --       h2 = Handler $ \ (e :: AnotherException) -> return True
diff --git a/test/main.hs b/test/main.hs
new file mode 100644
--- /dev/null
+++ b/test/main.hs
@@ -0,0 +1,2 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+
