diff --git a/ini.cabal b/ini.cabal
--- a/ini.cabal
+++ b/ini.cabal
@@ -1,5 +1,5 @@
 name:                ini
-version:             0.3.6
+version:             0.4.0
 synopsis:            Quick and easy configuration files in the INI format.
 description:         Quick and easy configuration files in the INI format.
 license:             BSD3
@@ -22,6 +22,17 @@
                      attoparsec,
                      text,
                      unordered-containers
+  if !impl(ghc >= 8)
+    build-depends: semigroups >= 0.10 && < 0.19
+
+test-suite ini-test
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Main.hs
+  build-depends:     base >= 4 && <5
+                   , ini
+                   , hspec
+                   , unordered-containers
 
 source-repository head
   type:     git
diff --git a/src/Data/Ini.hs b/src/Data/Ini.hs
--- a/src/Data/Ini.hs
+++ b/src/Data/Ini.hs
@@ -42,7 +42,9 @@
    readIniFile
   ,parseIni
   ,lookupValue
+  ,lookupArray
   ,readValue
+  ,readArray
   ,parseValue
   ,sections
   ,keys
@@ -57,6 +59,7 @@
   ,writeIniFileWith
    -- * Types
   ,Ini(..)
+  ,unIni
    -- * Parsers
   ,iniParser
   ,sectionParser
@@ -71,17 +74,34 @@
 import           Data.Char
 import           Data.HashMap.Strict        (HashMap)
 import qualified Data.HashMap.Strict        as M
-import           Data.Semigroup
+import Data.Maybe
 import           Data.Monoid (Monoid)
+import           Data.Semigroup
 import           Data.Text                  (Text)
 import qualified Data.Text                  as T
 import qualified Data.Text.IO               as T
 import           Prelude                    hiding (takeWhile)
 
 -- | An INI configuration.
-newtype Ini = Ini { unIni :: HashMap Text (HashMap Text Text) }
-  deriving (Show, Semigroup, Monoid)
+data Ini =
+  Ini
+    { iniSections :: HashMap Text [(Text, Text)]
+    , iniGlobals  :: [(Text, Text)]
+    }
+  deriving (Show, Eq)
 
+instance Semigroup Ini where
+  (<>) = mappend
+
+instance Monoid Ini where
+  mempty = Ini {iniGlobals = mempty, iniSections = mempty}
+  mappend x y =
+    Ini {iniGlobals = mempty, iniSections = iniSections x <> iniSections y}
+
+{-# DEPRECATED #-}
+unIni :: Ini -> HashMap Text (HashMap Text Text)
+unIni = fmap M.fromList . iniSections
+
 -- | Parse an INI file.
 readIniFile :: FilePath -> IO (Either String Ini)
 readIniFile = fmap parseIni . T.readFile
@@ -90,36 +110,89 @@
 parseIni :: Text -> Either String Ini
 parseIni = parseOnly iniParser
 
--- | Lookup values in the config.
-lookupValue :: Text -> Text -> Ini -> Either String Text
-lookupValue name key (Ini ini) =
-  case M.lookup name ini of
+-- | Lookup one value in the config.
+--
+-- Example:
+--
+-- >>> parseIni "[SERVER]\nport: 6667\nhostname: localhost" >>= lookupValue "SERVER" "hostname"
+-- Right "localhost"
+lookupValue :: Text -- ^ Section name
+            -> Text -- ^ Key
+            -> Ini -> Either String Text
+lookupValue name key (Ini {iniSections=secs}) =
+  case M.lookup name secs of
     Nothing -> Left ("Couldn't find section: " ++ T.unpack name)
     Just section ->
-      case M.lookup key section of
+      case lookup key section of
         Nothing -> Left ("Couldn't find key: " ++ T.unpack key)
         Just value -> return value
 
+-- | Lookup one value in the config.
+--
+-- Example:
+--
+-- >>> parseIni "[SERVER]\nport: 6667\nhostname: localhost" >>= lookupValue "SERVER" "hostname"
+-- Right "localhost"
+lookupArray :: Text -- ^ Section name
+            -> Text -- ^ Key
+            -> Ini -> Either String [Text]
+lookupArray name key (Ini {iniSections = secs}) =
+  case M.lookup name secs of
+    Nothing -> Left ("Couldn't find section: " ++ T.unpack name)
+    Just section ->
+      case mapMaybe
+             (\(k, v) ->
+                if k == key
+                  then Just v
+                  else Nothing)
+             section of
+        [] -> Left ("Couldn't find key: " ++ T.unpack key)
+        values -> return values
+
 -- | Get the sections in the config.
+--
+-- Example:
+--
+-- >>> sections <$> parseIni "[SERVER]\nport: 6667\nhostname: localhost"
+-- Right ["SERVER"]
 sections :: Ini -> [Text]
-sections (Ini ini) = M.keys ini
+sections = M.keys . iniSections
 
 -- | Get the keys in a section.
-keys :: Text -> Ini -> Either String [Text]
-keys name (Ini ini) =
-  case M.lookup name ini of
+--
+-- Example:
+--
+-- >>> parseIni "[SERVER]\nport: 6667\nhostname: localhost" >>= keys "SERVER"
+-- Right ["hostname","port"]
+keys :: Text -- ^ Section name
+     -> Ini -> Either String [Text]
+keys name i =
+  case M.lookup name (iniSections i) of
     Nothing -> Left ("Couldn't find section: " ++ T.unpack name)
-    Just section -> Right (M.keys section)
+    Just section -> Right (map fst section)
 
 -- | Read a value using a reader from "Data.Text.Read".
-readValue :: Text -> Text -> (Text -> Either String (a, Text))
+readValue :: Text -- ^ Section name
+          -> Text -- ^ Key
+          -> (Text -> Either String (a, Text))
           -> Ini
           -> Either String a
 readValue section key f ini =
   lookupValue section key ini >>= f >>= return . fst
 
+-- | Read an array of values using a reader from "Data.Text.Read".
+readArray :: Text -- ^ Section name
+          -> Text -- ^ Key
+          -> (Text -> Either String (a, Text))
+          -> Ini
+          -> Either String [a]
+readArray section key f ini =
+  fmap (map fst) (lookupArray section key ini >>= mapM f)
+
 -- | Parse a value using a reader from "Data.Attoparsec.Text".
-parseValue :: Text -> Text -> Parser a
+parseValue :: Text -- ^ Section name
+           -> Text -- ^ Key
+           -> Parser a
            -> Ini
            -> Either String a
 parseValue section key f ini =
@@ -156,11 +229,11 @@
 
 -- | Print an INI config.
 printIniWith :: WriteIniSettings -> Ini -> Text
-printIniWith wis (Ini ini) =
-  T.concat (map buildSection (M.toList ini))
+printIniWith wis i =
+  T.concat (map buildSection (M.toList (iniSections i)))
   where buildSection (name,pairs) =
           "[" <> name <> "]\n" <>
-          T.concat (map buildPair (M.toList pairs))
+          T.concat (map buildPair pairs)
         buildPair (name,value) =
           name <> separator <> value <> "\n"
         separator = case writeIniKeySeparator wis of
@@ -169,10 +242,13 @@
 
 -- | Parser for an INI.
 iniParser :: Parser Ini
-iniParser = fmap (Ini . M.fromList) (many sectionParser)
+iniParser =
+  (\kv secs -> Ini {iniSections = M.fromList secs, iniGlobals = kv}) <$>
+  many keyValueParser <*>
+  many sectionParser
 
 -- | A section. Format: @[foo]@. Conventionally, @[FOO]@.
-sectionParser :: Parser (Text,HashMap Text Text)
+sectionParser :: Parser (Text,[(Text, Text)])
 sectionParser =
   do skipEndOfLine
      skipComments
@@ -182,7 +258,7 @@
      _ <- char ']'
      skipEndOfLine
      values <- many keyValueParser
-     return (T.strip name, M.fromList values)
+     return (T.strip name, values)
 
 -- | A key-value pair. Either @foo: bar@ or @foo=bar@.
 keyValueParser :: Parser (Text,Text)
@@ -204,7 +280,7 @@
 
 -- | Skip end of line and whitespace beyond.
 skipEndOfLine :: Parser ()
-skipEndOfLine = skipWhile (\c -> isEndOfLine c)
+skipEndOfLine = skipWhile isSpace
 
 -- | Skip comments starting at the beginning of the line.
 skipComments :: Parser ()
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- | Simple test suite.
+
+module Main where
+
+import qualified Data.HashMap.Strict as HM
+import           Data.Ini
+import           Test.Hspec
+
+main :: IO ()
+main =
+  hspec
+    (do describe
+          "Regular files"
+          (do it
+                "Multi-section file with comments"
+                (shouldBe
+                   (parseIni
+                      "# Some comment.\n\
+                      \[SERVER]\n\
+                      \port=6667\n\
+                      \hostname=localhost\n\
+                      \[AUTH]\n\
+                      \user=hello\n\
+                      \pass=world\n\
+                      \# Salt can be an empty string.\n\
+                      \salt=")
+                   (Right
+                      (Ini
+                         { iniSections =
+                             HM.fromList
+                               [ ( "AUTH"
+                                 , [ ("user", "hello")
+                                   , ("pass", "world")
+                                   , ("salt", "")
+                                   ])
+                               , ( "SERVER"
+                                 , [("port", "6667"), ("hostname", "localhost")])
+                               ]
+                         , iniGlobals = []
+                         })))
+              it
+                "File with globals"
+                (shouldBe
+                   (parseIni
+                      "# Some comment.\n\
+                      \port=6667\n\
+                      \hostname=localhost\n\
+                      \[AUTH]\n\
+                      \user=hello\n\
+                      \pass=world\n\
+                      \# Salt can be an empty string.\n\
+                      \salt=")
+                   (Right
+                      (Ini
+                         { iniSections =
+                             HM.fromList
+                               [ ( "AUTH"
+                                 , [ ("user", "hello")
+                                   , ("pass", "world")
+                                   , ("salt", "")
+                                   ])
+                               ]
+                         , iniGlobals =
+                             [("port", "6667"), ("hostname", "localhost")]
+                         })))))
