diff --git a/Data/Bimap.hs b/Data/Bimap.hs
--- a/Data/Bimap.hs
+++ b/Data/Bimap.hs
@@ -1,3 +1,9 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
+#if __GLASGOW_HASKELL__ >= 708
+{-# LANGUAGE TypeFamilies #-}
+#endif
+
 {-|
 An implementation of bidirectional maps between values of two
 key types. A 'Bimap' is essentially a bijection between subsets of
@@ -30,12 +36,22 @@
     lookupR,
     (!),
     (!>),
+    (!?),
+    (!?>),
     -- * Construction
     empty,
     singleton,
     -- * Update
     insert,
     tryInsert,
+    adjust,
+    adjustR,
+    adjustWithKey,
+    adjustWithKeyR,
+    update,
+    updateR,
+    updateWithKey,
+    updateWithKeyR,
     delete,
     deleteR,
     -- * Min\/Max
@@ -67,6 +83,10 @@
     elems,
     assocs,
     fold,
+    Data.Bimap.map,
+    mapR,
+    mapMonotonic,
+    mapMonotonicR,
     toMap,
     toMapR,
     -- * Miscellaneous
@@ -75,12 +95,24 @@
     twisted,
 ) where
 
-import Data.List (foldl', sort)
-import qualified Data.Map as M
-import Prelude hiding (lookup, null, filter, pred)
-import qualified Prelude as P
+import           Control.DeepSeq     (NFData)
+import           Control.Monad.Catch
 
+import           Data.Function       (on)
+import           Data.List           (foldl', sort)
+import qualified Data.Map            as M
+import           Data.Maybe          (fromMaybe)
+import           Data.Typeable
 
+#if __GLASGOW_HASKELL__ >= 708
+import qualified Data.BimapExt       as GHCExts
+#endif
+import           GHC.Generics        (Generic)
+
+import           Prelude             hiding (filter, lookup, null, pred)
+import qualified Prelude             as P
+
+
 infixr 9 .:
 (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
 (.:) = (.).(.)
@@ -88,14 +120,34 @@
 {-|
 A bidirectional map between values of types @a@ and @b@.
 -}
-data Bimap a b = MkBimap !(M.Map a b) !(M.Map b a)
+data Bimap a b = MkBimap !(M.Map a b) !(M.Map b a) deriving (Generic)
 
 instance (Show a, Show b) => Show (Bimap a b) where
     show x = "fromList " ++ (show . toList $ x)
 
 instance (Eq a, Eq b) => Eq (Bimap a b) where
-    (==) bx by = toAscList bx == toAscList by
+    (==) = (==) `on` toAscList
 
+instance (Ord a, Ord b) => Ord (Bimap a b) where
+    compare = compare `on` toAscList
+
+instance (NFData a, NFData b) => NFData (Bimap a b)
+
+#if __GLASGOW_HASKELL__ >= 708
+instance (Ord a, Ord b) => GHCExts.IsList (Bimap a b) where
+    type Item (Bimap a b) = (a, b)
+    fromList = fromList
+    toList = toList
+#endif
+
+{-|
+A 'Bimap' action failed.
+-}
+data BimapException = KeyNotFound String
+  deriving(Eq, Show, Typeable)
+
+instance Exception BimapException
+
 {-| /O(1)/. The empty bimap.
 /Version: 0.2/-}
 empty :: Bimap a b
@@ -174,7 +226,7 @@
 tryInsert :: (Ord a, Ord b)
           => a -> b -> Bimap a b -> Bimap a b
 tryInsert x y bi
-    | (x `notMember` bi && y `notMemberR` bi) = unsafeInsert x y bi
+    | x `notMember` bi && y `notMemberR` bi = unsafeInsert x y bi
     | otherwise                               = bi
 
 {-| /O(log n)/.
@@ -194,12 +246,12 @@
        => Either a b -> Bimap a b -> Bimap a b
 deleteE e (MkBimap left right) =
     MkBimap
-        (perhaps M.delete x $ left)
-        (perhaps M.delete y $ right)
+        (perhaps M.delete x  left)
+        (perhaps M.delete y  right)
     where
     perhaps = maybe id
-    x = either Just (flip M.lookup right) e
-    y = either (flip M.lookup left) Just  e
+    x = either Just (`M.lookup` right) e
+    y = either (`M.lookup` left) Just  e
 
 {-| /O(log n)/.
 Delete a value and its twin from a bimap.
@@ -217,28 +269,107 @@
 deleteR = deleteE . Right
 
 {-| /O(log n)/.
+Update a value at a specific left key with the result of the provided function.
+
+When the left key is not a member of the bimap, the original bimap is returned.-}
+adjust :: (Ord a, Ord b) => (b -> b) -> a -> Bimap a b -> Bimap a b
+adjust f = adjustWithKey (const f)
+
+{-| /O(log n)/.
+Update a value at a specific right key with the result of the provided function.
+
+When the right key is not a member of the bimap, the original bimap is returned.-}
+adjustR :: (Ord a, Ord b) => (a -> a) -> b -> Bimap a b -> Bimap a b
+adjustR f b = reverseBimap . adjust f b . reverseBimap
+  where reverseBimap (MkBimap left right) = MkBimap right left
+
+{-| /O(log n)/.
+Adjust a value at a specific left key.
+
+When the left key is not a member of the bimap, the original bimap is returned.-}
+adjustWithKey :: (Ord a, Ord b) => (a -> b -> b) -> a -> Bimap a b -> Bimap a b
+adjustWithKey f = updateWithKey (\a -> Just . f a)
+
+{-| /O(log n)/.
+Adjust a value at a specific right key.
+
+When the right key is not a member of the bimap, the original bimap is returned.-}
+adjustWithKeyR :: (Ord a, Ord b) => (b -> a -> a) -> b -> Bimap a b -> Bimap a b
+adjustWithKeyR f b = reverseBimap . adjustWithKey f b . reverseBimap
+  where reverseBimap (MkBimap left right) = MkBimap right left
+
+{-| /O(log n)/.
+The expression (@'update' f a bimap@) updates the right value @b@ at @a@ (if it is in the bimap).
+
+If (@f b@) is 'Nothing', the element is deleted.
+
+If it is (@'Just' y@), the left key @a@ is bound to the new value @y@.-}
+update :: (Ord a, Ord b) => (b -> Maybe b) -> a -> Bimap a b -> Bimap a b
+update f = updateWithKey (const f)
+
+{-| /O(log n)/.
+The expression (@'updateR' f b bimap@) updates the left value @a@ at @b@ (if it is in the bimap).
+
+If (@f a@) is 'Nothing', the element is deleted.
+
+If it is (@'Just' x@), the right key @b@ is bound to the new value @x@.-}
+updateR :: (Ord a, Ord b) => (a -> Maybe a) -> b -> Bimap a b -> Bimap a b
+updateR f b = reverseBimap . update f b . reverseBimap
+  where reverseBimap (MkBimap left right) = MkBimap right left
+
+{-| /O(log n)/.
+The expression (@'updateWithKey' f a bimap@) updates the right value @b@ at @a@ (if it is in the bimap).
+
+If (@f a b@) is 'Nothing', the element is deleted.
+
+If it is (@'Just' y@), the left key @a@ is bound to the new value @y@.-}
+updateWithKey :: (Ord a, Ord b) => (a -> b -> Maybe b) -> a -> Bimap a b -> Bimap a b
+updateWithKey f a (MkBimap left right) = MkBimap left' right' where
+  oldB = M.lookup a left
+  newB = f a =<< oldB
+  oldA = newB >>= (`M.lookup` right) >>= \x -> if x == a then Nothing else Just x
+  left' = maybe id M.delete oldA $ M.updateWithKey f a left
+  right' = maybe id (`M.insert` a) newB $ maybe id M.delete oldB right
+
+{-| /O(log n)/.
+The expression (@'updateWithKeyR' f b bimap@) updates the left value @a@ at @b@ (if it is in the bimap).
+
+If (@f b a@) is 'Nothing', the element is deleted.
+
+If it is (@'Just' x@), the right key @b@ is bound to the new value @x@.-}
+updateWithKeyR :: (Ord a, Ord b) => (b -> a -> Maybe a) -> b -> Bimap a b -> Bimap a b
+updateWithKeyR f b = reverseBimap . updateWithKey f b . reverseBimap
+  where reverseBimap (MkBimap left right) = MkBimap right left
+
+
+{-| /O(log n)/.
 Lookup a left key in the bimap, returning the associated right key.
 
 This function will @return@ the result in the monad, or @fail@ if
 the value isn't in the bimap.
 
+Note that the signature differs slightly from Data.Map's @lookup@. This one is more general -
+it functions the same way as the "original" if @m@ is cast (or inferred) to Maybe.
+
 /Version: 0.2/-}
-lookup :: (Ord a, Ord b, Monad m)
-        => a -> Bimap a b -> m b
+lookup :: (Ord a, Ord b, MonadThrow m)
+       => a -> Bimap a b -> m b
 lookup x (MkBimap left _) =
-    maybe (fail "Data.Bimap.lookup: Left key not found")
-          (return)
+    maybe (throwM $ KeyNotFound "Data.Bimap.lookup")
+          return
           (M.lookup x left)
 
 {-| /O(log n)/.
 A version of 'lookup' that is specialized to the right key,
 and returns the corresponding left key.
+
+
 /Version: 0.2/-}
-lookupR :: (Ord a, Ord b, Monad m)
+lookupR :: (Ord a, Ord b, MonadThrow m)
         => b -> Bimap a b -> m a
 lookupR y (MkBimap _ right) =
-    maybe (fail "Data.Bimap.lookupR: Right key not found")
-          (return)
+    maybe (throwM $ KeyNotFound "Data.Bimap.lookupR")
+          return
           (M.lookup y right)
 
 {-| /O(log n)/.
@@ -246,26 +377,32 @@
 Calls @'error'@ when the key is not in the bimap.
 /Version: 0.2/-}
 (!) :: (Ord a, Ord b) => Bimap a b -> a -> b
-(!) bi x = case lookup x bi of
-    Just y  -> y
-    Nothing -> error "Data.Bimap.(!): Left key not found"
+(!) bi x = fromMaybe (error "Data.Bimap.(!): Left key not found") $ lookup x bi
 
 {-| /O(log n)/.
 A version of @(!)@ that is specialized to the right key,
 and returns the corresponding left key.
 /Version: 0.2/-}
 (!>) :: (Ord a, Ord b) => Bimap a b -> b -> a
-(!>) bi y = case lookupR y bi of
-    Just x  -> x
-    Nothing -> error "Data.Bimap.(!>): Right key not found"
+(!>) bi y = fromMaybe (error "Data.Bimap.(!>): Right key not found") $ lookupR y bi
 
+{-| /O(log n)/.
+See 'lookup'. -}
+(!?) :: (Ord a, Ord b, MonadThrow m) => Bimap a b -> a -> m b
+(!?) = flip lookup
+
+{-| /O(log n)/.
+See 'lookupR'. -}
+(!?>) :: (Ord a, Ord b, MonadThrow m) => Bimap a b -> b -> m a
+(!?>) = flip lookupR
+
 {-| /O(n*log n)/.
 Build a map from a list of pairs. If there are any overlapping
 pairs in the list, the later ones will override the earlier ones.
 /Version: 0.2/-}
 fromList :: (Ord a, Ord b)
          => [(a, b)] -> Bimap a b
-fromList xs = foldl' (flip . uncurry $ insert) empty xs
+fromList = foldl' (flip . uncurry $ insert) empty
 
 {-| /O(n*log n)/.
 Build a map from a list of pairs. Unlike 'fromList', earlier pairs
@@ -281,7 +418,7 @@
 /Version: 0.2.2/-}
 fromAList :: (Ord a, Ord b)
           => [(a, b)] -> Bimap a b
-fromAList xs = foldl' (flip . uncurry $ tryInsert) empty xs
+fromAList = foldl' (flip . uncurry $ tryInsert) empty
 
 {-| /O(n)/. Convert to a list of associated pairs.
 /Version: 0.2/-}
@@ -324,7 +461,7 @@
                          => [(a, b)] -> Bimap a b
 fromAscPairListUnchecked xs = MkBimap
     (M.fromAscList xs)
-    (M.fromAscList $ map swap  xs)
+    (M.fromAscList $ P.map swap  xs)
     where
     swap (x, y) = (y, x)
 
@@ -427,7 +564,7 @@
     [ M.valid left, M.valid right
     , (==)
         (sort .                M.toList $ left )
-        (sort . map flipPair . M.toList $ right)
+        (sort . P.map flipPair . M.toList $ right)
     ]
     where
     flipPair (x, y) = (y, x)
@@ -452,6 +589,36 @@
 fold :: (a -> b -> c -> c) -> c -> Bimap a b -> c
 fold f z = foldr (uncurry f) z . assocs
 
+{-| /O(n*log n)/
+Map a function over all the left keys in the map.
+/Version 0.3/-}
+map :: Ord c => (a -> c) -> Bimap a b -> Bimap c b
+map f (MkBimap left right) =
+    MkBimap (M.mapKeys f left) (M.map f right)
+
+{-| /O(n*log n)/
+Map a function over all the right keys in the map.
+/Version 0.3/-}
+mapR :: Ord c => (b -> c) -> Bimap a b -> Bimap a c
+mapR f (MkBimap left right) =
+    MkBimap (M.map f left) (M.mapKeys f right)
+
+{-| /O(n)/.
+Map a strictly increasing function over all left keys in the map.
+/The precondition is not checked./
+/Version 0.3/-}
+mapMonotonic :: (a -> c) -> Bimap a b -> Bimap c b
+mapMonotonic f (MkBimap left right) =
+    MkBimap (M.mapKeysMonotonic f left) (M.map f right)
+
+{-| /O(n)/.
+Map a strictly increasing function over all right keys in the map.
+/The precondition is not checked./
+/Version 0.3/-}
+mapMonotonicR :: (b -> c) -> Bimap a b -> Bimap a c
+mapMonotonicR f (MkBimap left right) =
+    MkBimap (M.map f left) (M.mapKeysMonotonic f right)
+
 {-| /O(log n)/.
 Delete and find the element with maximal left key.
 Calls @'error'@ if the bimap is empty.
@@ -475,7 +642,7 @@
 /Version: 0.2.2/-}
 deleteMax :: (Ord b) => Bimap a b -> Bimap a b
 deleteMax = snd . deleteFindMax
- 
+
 {-| /O(log n)/.
 Delete the element with maximal right key.
 Calls @'error'@ if the bimap is empty.
@@ -491,7 +658,7 @@
 findMax = M.findMax . toMap
 
 {-| /O(log n)/.
-Find the element with maximal right key. The 
+Find the element with maximal right key. The
 right-hand key is the first entry in the pair.
 Calls @'error'@ if the bimap is empty.
 /Version: 0.2.2/-}
@@ -521,7 +688,7 @@
 /Version: 0.2.2/-}
 deleteMin :: (Ord b) => Bimap a b -> Bimap a b
 deleteMin = snd . deleteFindMin
- 
+
 {-| /O(log n)/.
 Delete the element with minimal right key.
 Calls @'error'@ if the bimap is empty.
@@ -537,10 +704,9 @@
 findMin = M.findMin . toMap
 
 {-| /O(log n)/.
-Find the element with minimal right key. The 
+Find the element with minimal right key. The
 right-hand key is the first entry in the pair.
 Calls @'error'@ if the bimap is empty.
 /Version: 0.2.2/-}
 findMinR :: Bimap a b -> (b, a)
 findMinR = M.findMin . toMapR
-
diff --git a/Data/BimapExt.hs b/Data/BimapExt.hs
new file mode 100644
--- /dev/null
+++ b/Data/BimapExt.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE Trustworthy #-}
+{-|
+An auxiliary module that exports the 'IsList' class from "GHC.Exts". We use
+this intermediate module to isolate a safe feature from an otherwise non-safe
+module, and prevent all of "Data.Bimap" from being marked as not safe just
+because we are importing "GHC.Exts".
+
+The module only exports a class, and the class does not define any methods in
+an unsafe way. We therefore consider it safe and mark this module as
+trustworthy.
+-}
+module Data.BimapExt (
+    IsList(..)
+) where
+
+import GHC.Exts (IsList(..))
diff --git a/HISTORY b/HISTORY
--- a/HISTORY
+++ b/HISTORY
@@ -1,3 +1,11 @@
+Version 0.3.1 (17 October 2015)
+
+  * Added update and adjust functions (thanks to koral)
+
+Version 0.3.0 (12 Mar 2015)
+
+  * Added map functions
+
 Version 0.2.4 (25 Aug 2008)
 
   * added filter and partition
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/Setup.lhs b/Setup.lhs
deleted file mode 100644
--- a/Setup.lhs
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env runhaskell
-> import Distribution.Simple
-> import System.Cmd
-> import System.Exit
-
-> main = defaultMainWithHooks (simpleUserHooks { runTests = suite })
->     where
->     suite _ _ _ _ = system "bash tests.sh" >> return ()
-
diff --git a/Test/RunTests.hs b/Test/RunTests.hs
--- a/Test/RunTests.hs
+++ b/Test/RunTests.hs
@@ -8,6 +8,11 @@
 
 import Test.Tests
 import Test.Util
+import System.Exit
 
 main :: IO ()
-main = $( extractTests "Test/Tests.hs" )
+main = do
+  allPassed <- $( extractTests "Test/Tests.hs" )
+  if allPassed
+    then exitSuccess
+    else exitFailure
diff --git a/Test/Tests.hs b/Test/Tests.hs
--- a/Test/Tests.hs
+++ b/Test/Tests.hs
@@ -1,11 +1,11 @@
 module Test.Tests where
 
-import Data.List (sort)
+import Data.List (nub, sort)
 import qualified Data.Set as S
-import Prelude hiding (null, lookup, filter)
+import Prelude hiding (null, lookup, filter,map)
 import qualified Prelude as P
 import Test.QuickCheck
-import Test.QuickCheck.Batch
+import Control.Applicative((<$>))
 
 import Data.Bimap
 
@@ -15,6 +15,9 @@
 instance (Ord a, Arbitrary a, Ord b, Arbitrary b)
     => Arbitrary (Bimap a b) where
     arbitrary = fromList `fmap` arbitrary
+
+instance (Ord a, CoArbitrary a, Ord b, CoArbitrary b)
+    => CoArbitrary (Bimap a b) where
     coarbitrary = coarbitrary . toList
 
 -- generator for filter/partition classification functions
@@ -28,6 +31,8 @@
         return $ FilterFunc
             ("(\\x y -> x - y < " ++ show pivot ++ ")")
             (\x y -> fromIntegral x - fromIntegral y < pivot)
+instance (Integral a, CoArbitrary a, Integral b, CoArbitrary b)
+    => CoArbitrary (FilterFunc a b) where
     coarbitrary _ gen = do
         x <- arbitrary
         coarbitrary (x :: Int) gen
@@ -56,8 +61,8 @@
     bi = fromList xs
     isMember x = x `pairMember` bi
     notUnique (x, y) = 
-        ((>1) . length . P.filter (== x) . map fst $ xs) ||
-        ((>1) . length . P.filter (== y) . map snd $ xs)
+        ((>1) . length . P.filter (== x) . P.map fst $ xs) ||
+        ((>1) . length . P.filter (== y) . P.map snd $ xs)
 
 -- a bimap created from a list is no larger than the list
 prop_fromList_size xs = (size $ fromList xs) <= length xs
@@ -66,24 +71,21 @@
 
 -- a monotone bimap can be reconstituted via fromAscPairList
 prop_fromAscPairList_reconstitute xs = and
-    [ (not . isBottom) bi'
-    , valid bi'
+    [ valid bi'
     , (bi == bi')
     ]
     where
-    xs' = zip (sort $ map fst xs) (sort $ map snd xs)
+    xs' = zip (sort $ P.map fst xs) (sort $ P.map snd xs)
     bi :: Bimap Int Integer
     bi = fromList xs'
     bi' = fromAscPairList . toAscList $ bi
 
 -- fromAscPairList will never produce an invalid bimap
-prop_fromAscPairList_check xs = or
-    [ isBottom bi
-    , valid bi
-    ]
+prop_fromAscPairList_check xs = valid bi
     where
+    xs' = zip (nub $ sort $ P.map fst xs) (nub $ sort $ P.map snd xs)
     bi :: Bimap Int Integer
-    bi = fromAscPairList xs
+    bi = fromAscPairList xs'
 
 -- if a pair is a member of the bimap, then both elements are present
 -- and associated with each other
@@ -161,6 +163,21 @@
     where
     _ = bi :: Bimap Int Integer
 
+-- adjust and fmap are similar
+prop_adjust_fmap bi a = l === r
+  where
+  l = lookup a $ adjust f a bi :: Maybe Integer
+  r = f <$> lookup a bi
+  _ = bi :: Bimap Int Integer
+  f = (1-)
+
+prop_adjustR_fmap bi b = l == r
+  where
+  l = lookupR b $ adjustR f b bi :: Maybe Int
+  r = f <$> lookupR b bi
+  _ = bi :: Bimap Int Integer
+  f = (3*)
+
 -- a singleton bimap is valid, has one association, and the two
 -- given values map to each other
 prop_singleton x y = let bi = singleton x y in and
@@ -442,3 +459,50 @@
     where
     _ = bi :: Bimap Int Integer
 
+prop_map_preserve_keys bi =
+    (Data.List.sort $ P.map f $ keys bi) == (keys $ map f bi)
+    where
+    f = (4/) -- This is an arbitrary function
+    _ = bi :: Bimap Double Integer
+
+prop_map_preserve_lookup bi v =
+    (lookup (f v) $ map f bi) == (lookup v bi :: Maybe Integer)
+    where
+    f = (1-)
+    _ = bi :: Bimap Int Integer
+
+prop_map_preserve_right_keys bi =
+    (Data.List.sort $ P.map f $ keysR bi) == (keysR $ mapR f bi)
+    where
+    f = (4/) -- This is an arbitrary function
+    _ = bi :: Bimap Int Double
+
+prop_map_preserve_lookupR bi v =
+    (lookup v $ mapR f bi) == (f <$> lookup v bi :: Maybe Integer)
+    where
+    f = (1-)
+    _ = bi :: Bimap Int Integer
+
+prop_mapMonotonic_preserve_keys bi =
+    (P.map f $ keys bi) == (keys $ mapMonotonic f bi)
+    where
+    f = (3+) -- This is an arbitrary monotonic function
+    _ = bi :: Bimap Double Integer
+
+prop_mapMonotonic_preserve_lookup bi v =
+    (lookup (f v) $ mapMonotonic f bi) == (lookup v bi :: Maybe Integer)
+    where
+    f = (2*)
+    _ = bi :: Bimap Int Integer
+
+prop_mapMontonic_preserve_right_keys bi =
+    (P.map f $ keysR bi) == (keysR $ mapMonotonicR f bi)
+    where
+    f = (^2) -- This is an arbitrary monotonic function
+    _ = bi :: Bimap Int Double
+
+prop_mapMonotonic_preserve_lookupR bi v =
+    (lookup v $ mapMonotonicR f bi) == (f <$> lookup v bi :: Maybe Integer)
+    where
+    f = (1-)
+    _ = bi :: Bimap Int Integer
diff --git a/Test/Util.hs b/Test/Util.hs
--- a/Test/Util.hs
+++ b/Test/Util.hs
@@ -30,19 +30,24 @@
     firstToken = fst . head . lex
     isProperty = isPrefixOf "prop_"
 
-{- Inspired by & borrowed from: -}
--- http://blog.codersbase.com/2006/09/01/simple-unit-testing-in-haskell/
-mkCheck name =
-    [| printf "%-25s : " name >> quickCheck $(varE (mkName name)) |]
+resultIsSuccess Success {} = True
+resultIsSuccess _ = False
 
-mkChecks []        = undefined -- if we don't have any tests, then the test suite is undefined right?
-mkChecks [name]    = mkCheck name
-mkChecks (name:ns) = [| $(mkCheck name) >> $(mkChecks ns) |]
+mkCheck' name = [| printf "%-25s : " name
+                   >> quickCheckResult $(varE (mkName name))
+                   >>= return . resultIsSuccess |]
+mkChecks' [] = undefined
+mkChecks' [name] = mkCheck' name
+mkChecks' (name:ns) = [| do
+                          this <- $(mkCheck' name)
+                          rest <- $(mkChecks' ns)
+                          return $ this && rest |]
 
+
 {-
 Extract the names of QuickCheck tests from a file, and splice in
 a sequence of calls to them. The module doing the splicing must
 also import the file being processed.
 -}
 extractTests :: FilePath -> Q Exp
-extractTests = (mkChecks =<<) . runIO . fileProperties
+extractTests = (mkChecks' =<<) . runIO . fileProperties
diff --git a/bimap.cabal b/bimap.cabal
--- a/bimap.cabal
+++ b/bimap.cabal
@@ -1,6 +1,6 @@
-cabal-version:       >= 1.2.3.0
+cabal-version:       >= 1.10
 name:                bimap
-version:             0.2.4
+version:             0.5.0
 synopsis:            Bidirectional mapping between two key types
 description:
   A data structure representing a bidirectional mapping between two
@@ -9,29 +9,50 @@
 category:            Data
 license:             BSD3
 license-file:        LICENSE
-copyright:           Stuart Cook and contributors 2008
-author:              Stuart Cook and contributors 2008
-maintainer:          scook0@gmail.com
-homepage:            http://code.haskell.org/bimap
--- This build-type is a lie, but we're only using hooks to specify
--- a test command, so there shouldn't be any problems.
+copyright:           Stuart Cook and contributors 2008, Joel Williamson 2015
+author:              Stuart Cook and contributors 2008, Joel Williamson 2015
+maintainer:          Joel Williamson <joel@joelwilliamson.ca>
+homepage:            https://github.com/joelwilliamson/bimap
 build-type:          Simple
-tested-with:         GHC ==6.6, GHC ==6.8.1
+tested-with:         GHC <= 8.6.4 && >= 7.0
 extra-source-files:
     HISTORY
-    tests.sh
-    Test/Tests.hs
-    Test/Util.hs
-    Test/RunTests.hs
 
-flag small-base
-
 Library
-  if flag(small-base)
-    build-depends:       base >= 3, containers
+  build-depends:       base >= 4 && <5, containers, deepseq, exceptions
+  if impl(ghc < 7.6.1)
+    build-depends: ghc-prim
+    default-extensions:  CPP
+                         DeriveGeneric
+                         TypeFamilies
   else
-    build-depends:       base < 3
+    default-extensions: CPP
+                        TypeFamilies
+  default-language:    Haskell98   
   ghc-options:         -Wall
   exposed-modules:
       Data.Bimap
 
+  if impl(ghc >= 7.8)
+    other-modules:
+      Data.BimapExt
+
+test-suite tests
+    type:            exitcode-stdio-1.0
+    main-is:         Test/RunTests.hs
+    other-modules:   Test.Tests
+                     Test.Util
+    build-depends:   base >= 4 && < 5,
+                     containers,
+                     deepseq,
+                     exceptions,
+                     QuickCheck >= 2 && < 3,
+                     template-haskell >= 2 && < 3
+    if impl(ghc < 7.6.1)
+      build-depends: ghc-prim
+  default-extensions:  TemplateHaskell
+  default-language:    Haskell98   
+
+source-repository head
+    type:         git
+    location:     https://github.com/joelwilliamson/bimap.git
diff --git a/tests.sh b/tests.sh
deleted file mode 100644
--- a/tests.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-rm .test.log 2>/dev/null
-runhaskell Test/RunTests.hs > .test.log || exit 1
-cat .test.log || exit 2
-grep Falsifiable .test.log >/dev/null && exit 3
-echo "~~ all tests passed ~~"
-exit 0
