diff --git a/hackage-db.cabal b/hackage-db.cabal
--- a/hackage-db.cabal
+++ b/hackage-db.cabal
@@ -1,5 +1,5 @@
 Name:                   hackage-db
-Version:                1.8
+Version:                1.9
 Copyright:              Peter Simons
 License:                BSD3
 License-File:           LICENSE
@@ -39,7 +39,9 @@
    quickly, but the inital cost of reading @00-index.tar@ is fairly
    high.
    .
-   This package is known to work on Linux and Mac OS X, but not Windows.
+   This package is known to work on Linux and Mac OS X, but it's
+   probably not going to work on Windows (because no-one tested it, as
+   far as I know).
 
 Source-Repository head
   Type:                 git
@@ -49,5 +51,6 @@
   Build-Depends:        base >= 3 && < 5, tar >= 0.4, Cabal, containers,
                         directory, filepath, bytestring, utf8-string
   hs-source-dirs:       src
-  Ghc-Options:          -Wall
-  Exposed-Modules:      Distribution.Hackage.DB
+  Exposed-Modules:      Distribution.Hackage.DB.Unparsed
+                        Distribution.Hackage.DB.Parsed
+                        Distribution.Hackage.DB
diff --git a/src/Distribution/Hackage/DB.hs b/src/Distribution/Hackage/DB.hs
--- a/src/Distribution/Hackage/DB.hs
+++ b/src/Distribution/Hackage/DB.hs
@@ -20,70 +20,7 @@
 
 import Data.Map
 import Data.Version
+import Distribution.Hackage.DB.Parsed
+import Distribution.Hackage.DB.Path
 import Distribution.Package
 import Distribution.PackageDescription
-
-import qualified Codec.Archive.Tar as Tar
-import Data.ByteString.Lazy.Char8 ( ByteString )
-import qualified Data.ByteString.Lazy.Char8 as BS8 ( readFile )
-import qualified Data.ByteString.Lazy as BSC ( unpack )
-import Data.String.UTF8 ( toString, fromRep )
-import Data.Maybe ( fromMaybe )
-import System.Directory ( getHomeDirectory )
-import System.FilePath ( joinPath, splitDirectories )
-import Distribution.Text ( simpleParse )
-import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
-
--- | A 'Map' representation of the Hackage database. For sake of
--- simplicity, we use 'String' rather than 'PackageName' to represent
--- the name of a package.
-
-type Hackage = Map String (Map Version GenericPackageDescription)
-
--- | Read the Hackage database from the location determined by 'hackagePath'
--- and return a 'Map' that provides fast access to its contents.
-
-readHackage :: IO Hackage
-readHackage = hackagePath >>= readHackage'
-
--- | Read the Hackage database from the given 'FilePath' and return a
--- 'Hackage' map that provides fast access to its contents.
-
-readHackage' :: FilePath -> IO Hackage
-readHackage' = fmap parseHackage . BS8.readFile
-
--- | Parse the contents of Hackage's @00-index.tar@ into a 'Hackage' map.
-
-parseHackage :: ByteString -> Hackage
-parseHackage = Tar.foldEntries addEntry empty (error . show) . Tar.read
-  where
-    decodeUTF8 :: ByteString -> String
-    decodeUTF8 = toString . fromRep . BSC.unpack
-
-    addEntry :: Tar.Entry -> Hackage -> Hackage
-    addEntry e db = case splitDirectories (Tar.entryPath e) of
-                        [".",".","@LongLink"] -> db
-                        path@[name,vers,_] -> case Tar.entryContent e of
-                                                Tar.NormalFile buf _ -> add name vers buf db
-                                                _                    -> error ("Hackage.DB.parseHackage: unexpected content type for " ++ show path)
-                        _                  -> db
-
-    add :: String -> String -> ByteString -> Hackage -> Hackage
-    add name version pkg = insertWith union name (singleton (pVersion version) (pPackage name pkg))
-
-    pPackage :: String -> ByteString -> GenericPackageDescription
-    pPackage name buf = case parsePackageDescription (decodeUTF8 buf) of
-                          ParseOk _ a     -> a
-                          ParseFailed err -> error ("Hackage.DB.parseHackage: cannot parse cabal file " ++ show name ++ ": " ++ show err)
-
-    pVersion :: String -> Version
-    pVersion str = fromMaybe (error $ "Hackage.DB.parseHackage: cannot parse version " ++ show str) (simpleParse str)
-
--- | Determine the default path of the Hackage database, which typically
--- resides at @"$HOME\/.cabal\/packages\/hackage.haskell.org\/00-index.tar"@.
--- Running the command @"cabal update"@ will keep that file up-to-date.
-
-hackagePath :: IO FilePath
-hackagePath = do
-  homedir <- getHomeDirectory
-  return $ joinPath [homedir, ".cabal", "packages", "hackage.haskell.org", "00-index.tar"]
diff --git a/src/Distribution/Hackage/DB/Parsed.hs b/src/Distribution/Hackage/DB/Parsed.hs
new file mode 100644
--- /dev/null
+++ b/src/Distribution/Hackage/DB/Parsed.hs
@@ -0,0 +1,65 @@
+{- |
+   Module      :  Distribution.Hackage.DB.Parsed
+   License     :  BSD3
+   Maintainer  :  simons@cryp.to
+   Stability   :  provisional
+   Portability :  portable
+
+   This module provides simple access to the Hackage database by means
+   of 'Map'.
+ -}
+
+module Distribution.Hackage.DB.Parsed
+  ( Hackage, readHackage, readHackage', parseHackage
+  , parseUnparsedHackage
+  )
+  where
+
+import qualified Data.ByteString.Lazy as BSC ( unpack )
+import Data.ByteString.Lazy.Char8 ( ByteString )
+import Data.Map
+import Data.String.UTF8 ( toString, fromRep )
+import Data.Version
+import qualified Distribution.Hackage.DB.Unparsed as Unparsed
+import Distribution.PackageDescription
+import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
+import Distribution.Text ( display )
+
+-- | A 'Map' representation of the Hackage database. For sake of
+-- simplicity, we use 'String' rather than 'PackageName' to represent
+-- the name of a package.
+
+type Hackage = Map String (Map Version GenericPackageDescription)
+
+-- | Read the Hackage database from the location determined by 'hackagePath'
+-- and return a 'Map' that provides fast access to its contents.
+
+readHackage :: IO Hackage
+readHackage = fmap parseUnparsedHackage Unparsed.readHackage
+
+-- | Read the Hackage database from the given 'FilePath' and return a
+-- 'Hackage' map that provides fast access to its contents.
+
+readHackage' :: FilePath -> IO Hackage
+readHackage' = fmap parseUnparsedHackage . Unparsed.readHackage'
+
+-- | Parse the contents of Hackage's @00-index.tar@ into a 'Hackage' map.
+
+parseHackage :: ByteString -> Hackage
+parseHackage = parseUnparsedHackage . Unparsed.parseHackage
+
+-- | Convert an 'Unparsed.Hackage' map into a parsed 'Hackage' map.
+
+parseUnparsedHackage :: Unparsed.Hackage -> Hackage
+parseUnparsedHackage = Data.Map.mapWithKey parsePackages
+  where
+    parsePackages :: String -> Map Version ByteString -> Map Version GenericPackageDescription
+    parsePackages name = Data.Map.mapWithKey (parsePackage name)
+
+    parsePackage :: String -> Version -> ByteString -> GenericPackageDescription
+    parsePackage name version buf = case parsePackageDescription (decodeUTF8 buf) of
+      ParseOk _ a     -> a
+      ParseFailed err -> error ("Hackage.DB.parseHackage: cannot parse cabal file " ++ show name ++ "-" ++ display version ++ ": " ++ show err)
+
+    decodeUTF8 :: ByteString -> String
+    decodeUTF8 = toString . fromRep . BSC.unpack
diff --git a/src/Distribution/Hackage/DB/Unparsed.hs b/src/Distribution/Hackage/DB/Unparsed.hs
new file mode 100644
--- /dev/null
+++ b/src/Distribution/Hackage/DB/Unparsed.hs
@@ -0,0 +1,71 @@
+{- |
+   Module      :  Distribution.Hackage.DB.Unparsed
+   License     :  BSD3
+   Maintainer  :  simons@cryp.to
+   Stability   :  provisional
+   Portability :  portable
+
+   This module provides simple access to the Hackage database by means
+   of 'Map'.
+ -}
+
+module Distribution.Hackage.DB.Unparsed
+  ( Hackage, readHackage, readHackage', parseHackage, hackagePath
+  )
+  where
+
+import qualified Codec.Archive.Tar as Tar
+import Data.ByteString.Lazy.Char8 ( ByteString )
+import qualified Data.ByteString.Lazy.Char8 as BS8 ( readFile )
+import Data.Map
+import Data.Maybe ( fromMaybe )
+import Data.Version
+import Distribution.Text ( simpleParse )
+import System.Directory ( getHomeDirectory )
+import System.FilePath ( joinPath, splitDirectories )
+
+-- | A 'Map' representation of the Hackage database. Every package name
+-- maps to a non-empty set of version, and for every version there is a
+-- Cabal file stored as a (lazy) 'ByteString'.
+
+type Hackage = Map String (Map Version ByteString)
+
+-- | Read the Hackage database from the location determined by 'hackagePath'
+-- and return a 'Map' that provides fast access to its contents.
+
+readHackage :: IO Hackage
+readHackage = hackagePath >>= readHackage'
+
+-- | Read the Hackage database from the given 'FilePath' and return a
+-- 'Hackage' map that provides fast access to its contents.
+
+readHackage' :: FilePath -> IO Hackage
+readHackage' = fmap parseHackage . BS8.readFile
+
+-- | Parse the contents of Hackage's @00-index.tar@ into a 'Hackage' map.
+
+parseHackage :: ByteString -> Hackage
+parseHackage = Tar.foldEntries addEntry empty (error . show) . Tar.read
+  where
+    addEntry :: Tar.Entry -> Hackage -> Hackage
+    addEntry e db = case splitDirectories (Tar.entryPath e) of
+                        [".",".","@LongLink"] -> db
+                        path@[name,vers,_] -> case Tar.entryContent e of
+                                                Tar.NormalFile buf _ -> add name vers buf db
+                                                _                    -> error ("Hackage.DB.parseHackage: unexpected content type for " ++ show path)
+                        _                  -> db
+
+    add :: String -> String -> ByteString -> Hackage -> Hackage
+    add name version pkg = insertWith union name (singleton (pVersion version) pkg)
+
+    pVersion :: String -> Version
+    pVersion str = fromMaybe (error $ "Hackage.DB.parseHackage: cannot parse version " ++ show str) (simpleParse str)
+
+-- | Determine the default path of the Hackage database, which typically
+-- resides at @"$HOME\/.cabal\/packages\/hackage.haskell.org\/00-index.tar"@.
+-- Running the command @"cabal update"@ will keep that file up-to-date.
+
+hackagePath :: IO FilePath
+hackagePath = do
+  homedir <- getHomeDirectory
+  return $ joinPath [homedir, ".cabal", "packages", "hackage.haskell.org", "00-index.tar"]
