diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,140 @@
+# Implicit parameters
+
+There are certain types of applications which are configurable where it makes
+sense to model this configurability as a global or semi-global set of
+configuration values that some or all parts of the program can "implicitly"
+access. These configuration values are called "implicit parameters".
+
+## `ImplicitParams` in Haskell
+
+Haskell already has support for implicit parameters via the `ImplicitParams`
+[extension][ImplicitParams]. However, `ImplicitParams` has several flaws and
+is barely used at all in modern Haskell code. Many Haskellers consider its
+(ab)use of `let`/`where` bindings to pass implicit parameters to be ugly.
+Also, it's questionable how "implicit" the implicit parameters of
+`ImplicitParams` actually are, as they show up in the context of the type
+signature of any function which uses them. There's also no way you can call a
+function taking an implicit parameter without passing it that parameter if it
+isn't already in context: i.e., there is no way to specify a "default" value
+for an implicit parameter if none is passed.
+
+## Motivating example
+
+`implicit-params` solves some of these problems and introduces new problems of
+its own. However, there is one particular use case which motivated me to
+develop `implicit-params` that isn't supported by the existing
+`ImplicitParams` extension. Imagine you have the following code:
+
+    app :: Config -> IO ()
+    app config = doStuffWith config
+    
+    defaultConfig :: Config
+    defaultConfig = ...
+
+Which is used by a program as follows:
+
+    main = app defaultConfig
+
+There are two problems with this code. One is that `app` has to manually plumb
+the `Config` value it was given around everywhere. One solution here would be
+for `app` to use a [Reader][Reader] monad internally, but that can complicate
+the code in some ways and it seems like overkill. If it used the
+`ImplicitParams` extension, the above code would look like this:
+
+    app :: (?config :: Config) => IO ()
+    app = doStuffWith ?config
+    
+    defaultConfig :: Config
+    defaultConfig = ...
+
+    main = let ?config = defaultConfig in app
+
+You can see why `ImplicitParams` isn't very highly regarded: it made the type
+signature of `app` longer, as well as the code that uses it. However, at least
+the internals of `app` will be somewhat nicer now as the `Config` value won't
+have to be manually plumbed around everywhere.
+
+## `data-default`
+
+The [`data-default`][data-default] package provides a type class `Default`
+which represents the class of types which have a "default" value. It has a
+single operation `def` which returns the default value for a given type (the
+type is given by type inference). Using `Default` the above code could be made
+a little nicer:
+
+    app :: (?config :: Config) => IO ()
+    app = doStuffWith ?config
+    
+    instance Default Config where def = ...
+
+    main = let ?config = def in app
+
+However, the above code is still so *ugly* considering what we're trying to
+do: all we want to do is run `app` with the defaults. This should be as simple
+as typing `app`, and only if we're overriding the defaults should the code
+need to be any longer than this. This is exactly what `implicit-params` does.
+If an implicit parameter is not explicitly given to a function which requires
+it, its value is given by `def` for the `Default` instance for the type of the
+parameter. And if the type does not have a `Default` instance, then it is a
+type error to call that function without explicitly setting the implicit
+parameter (but it will work fine if you do set it). This is how the above code
+looks using `implicit-params`:
+
+    app :: Implicit_ Config => IO ()
+    app = doStuffWith param_
+    
+    instance Default Config where def = defaultConfig
+
+    main = app
+
+Perfect! What if we want to pass a non-default config to `app`? That's easy
+too:
+
+    main = setParam_ (def {option = 1}) app
+
+`setParam_` even has an infix synonym `$~` which makes the above code even
+nicer:
+
+    main = app $~ def {option = 1}
+
+(Bonus points for not abusing `let`/`where` bindings.)
+
+### Named implicit parameters
+
+The above code uses unnamed implicit parameters, which will suffice for most
+code. Sometimes you might want to pass more than one implicit parameter of the
+same type to a single function, and for this you need some way of selecting
+the particular implicit parameter on which to operate. `implicit-params` uses
+type level [symbols][Symbols] for this, which require the `DataKinds`
+[extension][DataKinds].
+
+`Implicit_`, which is used in the examples above, denotes an unnamed implicit
+parameter. `Implicit "foo"` can be used to denote a named implicit parameter
+named `"foo"`. Named implicit parameters are slightly more awkward to use
+because they require passing [`Proxy`][Proxy] parameters to the `param` and
+`setParam` functions to specify the names of the implicit parameters on which
+they are to operate. See the Haddock documentation of the `Data.Implicit`
+module for more details.
+
+## Acknowledgements
+
+This wouldn't be possible without techniques that I learnt from
+[Edward Kmett][edwardk] and [Philip JF][philipjf]. In particular, this package
+uses ideas from Edward's packages [tagged][tagged], [constraints][constraints]
+and [reflection][reflection] and Philip's blog posts [Haskell Supports
+First-Class Instances][firstclass] and [Using Compiler Bugs for Fun and
+Profit: Introducing Cartesian Closed Constraints][profit].
+
+[ImplicitParams]: http://www.haskell.org/ghc/docs/latest/html/users_guide/other-type-extensions.html#implicit-parameters
+[Reader]: http://hackage.haskell.org/packages/archive/mtl/latest/doc/html/Control-Monad-Reader-Class.html
+[data-default]: http://hackage.haskell.org/package/data-default
+[Symbols]: http://www.haskell.org/ghc/docs/latest/html/libraries/base/GHC-TypeLits.html#t:Symbol
+[DataKinds]: http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/kind-polymorphism-and-promotion.html
+[Proxy]: http://hackage.haskell.org/packages/archive/tagged/latest/doc/html/Data-Proxy.html
+[edwardk]: http://comonad.com/reader/
+[philipjf]: http://joyoftypes.blogspot.com/
+[tagged]: http://hackage.haskell.org/package/tagged
+[constraints]: http://hackage.haskell.org/package/constraints
+[reflection]: http://hackage.haskell.org/package/reflection
+[firstclass]: http://joyoftypes.blogspot.com/2012/02/haskell-supports-first-class-instances.html
+[profit]: http://joyoftypes.blogspot.com/2013/01/using-compiler-bugs-for-fun-and-profit.html
diff --git a/implicit-params.cabal b/implicit-params.cabal
--- a/implicit-params.cabal
+++ b/implicit-params.cabal
@@ -1,5 +1,5 @@
 name:           implicit-params
-version:        0.1
+version:        0.2
 synopsis:       Named and unnamed implicit parameters with defaults.
 license:        BSD3
 license-file:   LICENSE
@@ -17,7 +17,8 @@
 
 extra-source-files:
   CONTRIBUTORS,
-  LICENSE
+  LICENSE,
+  README.md
 
 library
   hs-source-dirs:
@@ -27,8 +28,8 @@
     Data.Implicit
 
   build-depends:
-    base >= 4.6 && < 5,
-    data-default > 0.2 && < 1
+    base >= 4.3 && < 5,
+    data-default-class > 0 && < 0.1
 
   ghc-options: -Wall
 
diff --git a/src/Data/Implicit.hs b/src/Data/Implicit.hs
--- a/src/Data/Implicit.hs
+++ b/src/Data/Implicit.hs
@@ -1,11 +1,12 @@
-{-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE GADTs #-}
-{-# LANGUAGE KindSignatures #-}
-{-# LANGUAGE ImplicitParams #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE OverlappingInstances #-}
+#if __GLASGOW_HASKELL__ >= 706
+{-# LANGUAGE PolyKinds #-}
+#endif
 {-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE UndecidableInstances #-}
 
 {-|
@@ -55,32 +56,30 @@
 import "Data.Implicit"
 import "Data.Proxy"
 
-putFooBar :: ('Implicit' \"foo\" String, 'Implicit' \"bar\" String) => IO ()
-putFooBar = do
-    putStrLn $ \"foo was: \" ++ show foo
-    putStrLn $ \"bar was: \" ++ show bar
-
-foo :: 'Implicit' \"foo\" String => String
-foo = 'param' (Proxy :: Proxy \"foo\")
-
-bar :: 'Implicit' \"bar\" String => String
-bar = 'param' (Proxy :: Proxy \"bar\")
+foo :: Proxy \"foo\"
+foo = Proxy
 
-setFoo :: String -> ('Implicit' \"foo\" String => a) -> a
-setFoo = 'setParam' (Proxy :: Proxy \"foo\")
+bar :: Proxy \"bar\"
+bar = Proxy
 
-setBar :: String -> ('Implicit' \"bar\" String => a) -> a
-setBar = 'setParam' (Proxy :: Proxy \"bar\")
+putFooBar :: ('Implicit' \"foo\" String, 'Implicit' \"bar\" String) => IO ()
+putFooBar = do
+    putStrLn $ \"foo was: \" ++ show (param foo :: String)
+    putStrLn $ \"bar was: \" ++ show (param bar :: String)
 @
 
 The 'Implicit' constraint is the named equivalent of 'Implicit_'. It takes an
-additional argument of kind 'Symbol' (which requires the @DataKinds@
-extension; see the "GHC.TypeLits" module) to specify the name of the implicit
-parameter. 'param' and 'setParam' work like their unnamed counterparts
-'param_' and 'setParam_', but they also take a proxy argument to specify the
-name of the implicit parameter. The code above defines the wrappers @foo@ and
-@bar@ and @setFoo@ and @setBar@ around @param@ and @setParam@ respectively,
-which hide all the (slightly ugly) proxy stuff.
+additional argument @s@ to specify the name of the implicit parameter.
+Implicit parameters can be \"named\" using any type (of any kind, on compilers
+that support the @PolyKinds@ extension). (The above code uses type-level
+strings of the @Symbol@ kind from the "GHC.TypeLits" module, which is the
+recommended way to name implicit parameters. However, @Symbol@ requires the
+@DataKinds@ extension and at least version 7.8 of GHC (7.6 is broken; see GHC
+bug \#7502), so you are free to use other types of other kinds if you want to
+support older versions of GHC.) 'param' and 'setParam' work like their unnamed
+counterparts 'param_' and 'setParam_', but they also take a proxy argument to
+specify the name of the implicit parameter. The code above defines @foo@ and
+@bar@ to hide away the (slightly ugly) proxy stuff.
 
 >>> putFooBar
 foo was: ""
@@ -89,18 +88,18 @@
 Once again, the defaults of unspecified implicit parameters are given by the
 'Default' class.
 
->>> setFoo "hello, world" putFooBar
+>>> setParam foo "hello, world" putFooBar
 foo was: "hello, world"
 bar was: ""
 
->>> setBar "goodbye" $ setFoo "hello, world" putFooBar
+>>> setParam bar "goodbye" $ setParam foo "hello, world" putFooBar
 foo was: "hello, world"
 bar was: "goodbye"
 
-An infix version of @setParam@ is also provided, '$$~'. Using @$$~@, the above
+An infix version of @setParam@ is also provided, '~$'. Using @~$@, the above
 example would be:
 
->>> putFooBar $$~ (Proxy :: Proxy "foo", "hello, world") $$~ (Proxy :: Proxy "bar", "goodbye")
+>>> putFooBar ~$ foo ~$ bar $$ "goodbye" $$ "hello, world"
 foo was: "hello, world"
 bar was: "goodbye
 
@@ -110,7 +109,8 @@
     ( Implicit
     , param
     , setParam
-    , ($$~)
+    , (~$)
+    , ($$)
 
     , Implicit_
     , param_
@@ -119,9 +119,7 @@
     )
 where
 
-import           Data.Default (Default, def)
-import           GHC.TypeLits (Symbol)
-import           GHC.Exts (Any)
+import           Data.Default.Class (Default, def)
 import           Unsafe.Coerce (unsafeCoerce)
 
 
@@ -129,99 +127,76 @@
 -- | The constraint @'Implicit' \"foo\" String@ on a function @f@ indicates
 -- that an implicit parameter named @\"foo\"@ of type @String@ is passed to
 -- @f@.
---
--- The name @\"foo\"@ is a type of kind 'Symbol' (from the "GHC.TypeLits"
--- module). The @DataKinds@ extension is required to refer to 'Symbol'-kinded
--- types.
-class Implicit (s :: Symbol) a where
-    _param :: proxy s -> a
+class Implicit s a where
+    -- | 'param' retrieves the implicit parameter named @s@ of type @a@ from
+    -- the context @'Implicit' s a@. The name @s@ is specified by a proxy
+    -- argument passed to @param@.
+    param :: proxy s -> a
 
 
 ------------------------------------------------------------------------------
 instance Default a => Implicit s a where
-    _param _ = def
+    param _ = def
 
 
 ------------------------------------------------------------------------------
--- | 'param' retrieves the implicit parameter named @s@ of type @a@ from the
--- context @'Implicit' s a@. The name @s@ is specified by a proxy argument
--- passed to @param@.
-param :: Implicit s a => proxy s -> a
-param = _param
+newtype Param s a b = Param (Implicit s a => b)
 
 
 ------------------------------------------------------------------------------
 -- | 'setParam' supplies a value for an implicit parameter named @s@ to a
 -- function which takes a homotypic and homonymous implicit parameter. The
 -- name @s@ is specified by a proxy argument passed to @setParam@.
-setParam :: proxy s -> a -> (Implicit s a => b) -> b
-setParam = using
+setParam :: forall a b proxy s. proxy s -> a -> (Implicit s a => b) -> b
+setParam _ a f = unsafeCoerce (Param f :: Param s a b) (const a)
+{-# INLINE setParam #-}
 
 
 ------------------------------------------------------------------------------
 -- | An infix version of 'setParam' with flipped arguments.
-($$~) :: (Implicit s a => b) -> (proxy s, a) -> b
-($$~) f (proxy, a) = using proxy a f
-
-
-------------------------------------------------------------------------------
--- | The constraint @'Implicit_' String@ on a function @f@ indicates that an
--- unnamed implicit parameter of type @String@ is passed to @f@.
-type Implicit_ = Implicit Any
-
-
-------------------------------------------------------------------------------
--- | 'param_' retrieves the unnamed implicit parameter of type @a@ from the
--- context @'Implicit_' a@.
-param_ :: Implicit_ a => a
-param_ = param (Proxy :: Proxy Any)
-
-
-------------------------------------------------------------------------------
--- | 'setParam_' supplies a value for an unnamed implicit parameter to a
--- function which takes a homotypic implicit parameter.
-setParam_ :: a -> (Implicit_ a => b) -> b
-setParam_ = setParam (Proxy :: Proxy Any)
-
-
-------------------------------------------------------------------------------
--- | An infix version of 'setParam_' with flipped arguments.
-($~) :: (Implicit_ a => b) -> a -> b
-infixr 1 $~
-f $~ a = using (Proxy :: Proxy Any) a f
+(~$) :: forall a b proxy s. (Implicit s a => b) -> proxy s -> a -> b
+infixl 1 ~$
+(~$) f _ a = unsafeCoerce (Param f :: Param s a b) (const a)
+{-# INLINE (~$) #-}
 
 
 ------------------------------------------------------------------------------
-data Proxy (s :: Symbol) = Proxy
+-- | A left-associated version of '$'.
+($$) :: (a -> b) -> a -> b
+infixl 0 $$
+($$) = id
+{-# INLINE ($$) #-}
 
 
 ------------------------------------------------------------------------------
-newtype Lift a = Lift a
+-- | The constraint @'Implicit_' String@ on a function @f@ indicates that an
+-- unnamed implicit parameter of type @String@ is passed to @f@.
+class Implicit_ a where
+    param_ :: a
 
 
 ------------------------------------------------------------------------------
-newtype Tagged (s :: Symbol) a = Tagged a
+instance Default a => Implicit_ a where
+    -- | 'param_' retrieves the unnamed implicit parameter of type @a@ from
+    -- the context @'Implicit_' a@.
+    param_ = def
 
 
 ------------------------------------------------------------------------------
-data Dict c where
-    Dict :: c => Dict c
+newtype Param_ a b = Param_ (Implicit_ a => b)
 
 
 ------------------------------------------------------------------------------
-using :: proxy s -> a -> (Implicit s a => b) -> b
-using p a = with (unlift (dict p a))
-  where
-    with :: Dict c -> (c => b) -> b
-    with Dict b = b
-
-    unlift :: Dict (c (Lift p)) -> Dict (c p)
-    unlift = unsafeCoerce
-
-    dict :: proxy s -> a -> Dict (Implicit s (Lift a))
-    dict _ a' = let ?param = Tagged a' in Dict
+-- | 'setParam_' supplies a value for an unnamed implicit parameter to a
+-- function which takes a homotypic implicit parameter.
+setParam_ :: forall a b. a -> (Implicit_ a => b) -> b
+setParam_ a f = unsafeCoerce (Param_ f :: Param_ a b) a
+{-# INLINE setParam_ #-}
 
 
 ------------------------------------------------------------------------------
-instance (?param :: Tagged s a) => Implicit s (Lift a) where
-    _param _ = let Tagged a = ?param in Lift a
+-- | An infix version of 'setParam_' with flipped arguments.
+($~) :: forall a b. (Implicit_ a => b) -> a -> b
+infixl 1 $~
+f $~ a = unsafeCoerce (Param_ f :: Param_ a b) a
+{-# INLINE ($~) #-}
