diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+0.2.0:
+	* Add "reflect" and "labels" functions.
+	* Drop support for GHC <8.
+
 0.1.2:
 	* Add docs for "project" function.
 
diff --git a/labels.cabal b/labels.cabal
--- a/labels.cabal
+++ b/labels.cabal
@@ -1,5 +1,5 @@
 name:                labels
-version:             0.1.2
+version:             0.2.0
 synopsis:            Anonymous records via named tuples
 description:         Declare and access tuple fields with labels. An approach to anonymous records.
 homepage:            https://github.com/chrisdone/labels#readme
@@ -19,7 +19,7 @@
   ghc-options:         -Wall -O2
   exposed-modules:     Labels,
                        Labels.Internal
-  build-depends:       base >= 4.7 && < 5,
+  build-depends:       base >= 4.9.0.0 && < 5,
                        template-haskell
   default-language:    Haskell2010
 
diff --git a/src/Labels.hs b/src/Labels.hs
--- a/src/Labels.hs
+++ b/src/Labels.hs
@@ -3,6 +3,7 @@
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE OverloadedLabels #-}
 -- | Labels for fields in a tuple.
 --
 -- Enable these extensions:
@@ -64,6 +65,16 @@
 --
 -- >>> project (#bar := "hello", #foo := 3) :: ("foo" := Int, "bar" := String)
 -- (#foo := 3,#bar := "hello")
+--
+-- Reflection of labels
+--
+-- >>> labels (#bar := "hello", #foo := 3, #mu := "hi")
+-- ["bar","foo","mu"]
+--
+-- Reflection of labelled fields
+--
+-- >>> reflect @Show show (#bar := "hello", #foo := 3, #mu := "hi")
+-- [("bar","\"hello\""),("foo","3"),("mu","\"hi\"")]
 
 module Labels
 -- Field access
@@ -73,11 +84,14 @@
   , lens
   , cons
   , project
+  , reflect
+  , labels
    -- Construction
   , (:=)(..)
   , Has
   , Cons
-  , Project)
+  , Project
+  , Reflect)
   where
 
 import Data.Proxy
diff --git a/src/Labels/Internal.hs b/src/Labels/Internal.hs
--- a/src/Labels/Internal.hs
+++ b/src/Labels/Internal.hs
@@ -1,3 +1,6 @@
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE TypeFamilies #-}
@@ -12,11 +15,11 @@
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE FlexibleInstances #-}
-#if __GLASGOW_HASKELL__ >= 800
-{-# LANGUAGE OverloadedLabels #-}
-#endif
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE OverloadedLabels #-}
+
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 -- | This module provides a way to name the fields in a regular
@@ -26,14 +29,10 @@
 
 import Data.Data
 import Data.String
+import GHC.Exts
 import GHC.TypeLits
 import Language.Haskell.TH
-
-#if __GLASGOW_HASKELL__ >= 800
 import GHC.OverloadedLabels
-#else
-import Data.Proxy
-#endif
 
 --------------------------------------------------------------------------------
 -- A labelled value
@@ -61,12 +60,10 @@
 --------------------------------------------------------------------------------
 -- Labels
 
-#if __GLASGOW_HASKELL__ >= 800
 instance l ~ l' =>
          IsLabel (l :: Symbol) (Proxy l') where
     fromLabel _ = Proxy
     {-# INLINE fromLabel #-}
-#endif
 
 instance IsString (Q Exp) where
   fromString str = [|Proxy :: Proxy $(litT (return (StrTyLit str)))|]
@@ -106,11 +103,67 @@
 -- | A record can be narrowed or have its order changed by projecting
 -- into record type.
 class Project from to where
-  -- | Narrow number of or change order of fields in a record.
+  -- | Narrow number of or change order of fields in a record:
+  -- Example: @project (#foo := 1, #bar := 2) :: ("bar" := Int)@
   project :: from -> to
 
 --------------------------------------------------------------------------------
+-- Key-value reflection
+
+-- | Reflection on labelled fields.
+class Reflect (c :: * -> Constraint) r where
+  -- | Produce a list of field names, and each field applied to the
+  -- given function. Example: @reflect \@Show show (#bar := "hello", #foo := 3)@
+  reflect :: forall b. (forall a. c a => a -> b) -> r -> [(String, b)]
+
+--------------------------------------------------------------------------------
+-- Field reflection
+
+-- | A constraint and a method which both simply ignore their
+-- argument.
+class Ignore a where ignore :: a -> ()
+instance Ignore a where ignore _ = ()
+
+-- | List the field labels present in the record. Example: @labels
+-- (#bar := "hello", #foo := 3, #mu := "hi")@
+labels :: Reflect Ignore r => r -> [String]
+labels r = map fst (reflect @Ignore ignore r)
+
+--------------------------------------------------------------------------------
 -- TH-derived instances
+
+-- Generate Reflect instances.
+$(let labelt i = varT (mkName ("l" ++ show i))
+      labelp i = varP (mkName ("l" ++ show i))
+      labelv i = varE (mkName ("l" ++ show i))
+      valuet i = varT (mkName ("v" ++ show i))
+      valuev i = varE (mkName ("v" ++ show i))
+      valuep i = varP (mkName ("v" ++ show i))
+      c = varT (mkName "c")
+      fp = varP (mkName "f")
+      fv = varE (mkName "f")
+  in sequence
+       [ instanceD
+         (sequence [[t|$(c) $(valuet i)|] | i <- [1 :: Int .. n]])
+         [t|Reflect $(c) $(foldl
+                             appT
+                             (tupleT n)
+                             [[t|$(labelt i) := $(valuet i)|] | i <- [1 .. n]])|]
+         [ funD
+             'reflect
+             [ clause
+                 [fp, tupP [conP '(:=) [labelp i,valuep i] | i <- [1 .. n]]]
+                 (normalB
+                    (listE
+                       [ [|(symbolVal $(labelv i), $(fv) $(valuev i))|]
+                       | i <- [1 .. n]
+                       ]))
+                 []
+             ]
+         , return (PragmaD (InlineP 'reflect Inline FunLike AllPhases))
+         ]
+       | n <- [1 .. 24]
+       ])
 
 -- Generate Cons instances.
 $(let makeInstance size =
