diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+0.2.3.0
+----------------
+* template-haskell bumped to 2.17
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# partial-isomorphism
+[![Travis branch](https://img.shields.io/travis/schernichkin/partial-isomorphisms.svg)](https://travis-ci.org/schernichkin/partial-isomorphisms)
+[![Hackage](https://img.shields.io/hackage/v/partial-isomorphisms.svg)](https://hackage.haskell.org/package/partial-isomorphisms)
diff --git a/partial-isomorphisms.cabal b/partial-isomorphisms.cabal
--- a/partial-isomorphisms.cabal
+++ b/partial-isomorphisms.cabal
@@ -1,50 +1,44 @@
-Name:                partial-isomorphisms
-Version:             0.2.2.1
-Synopsis:            Partial isomorphisms.
-Description:         Partial isomorphisms as described in the
-                     paper:
-                     .
-                     Tillmann Rendel and Klaus Ostermann.
-                     Invertible Syntax Descriptions:
-                     Unifying Parsing and Pretty Printing.
-                     In /Proc. of Haskell Symposium/, 2010.
-                     .
-                     The paper also describes invertible syntax
-                     descriptions as a common interface for
-                     parsers and pretty printers. These are
-                     distributed separately in the
-                     /invertible-syntax/ package.
-Homepage:            http://www.informatik.uni-marburg.de/~rendel/unparse
-License:             BSD3
-License-file:        LICENSE
-Author:              Tillmann Rendel
-Maintainer:          Tillmann Rendel <rendel@informatik.uni-marburg.de>
-                     Stanislav Chernichkin <schernichkin@gmail.com>
--- Copyright:
-Category:            Control
-Build-type:          Simple
--- Extra-source-files:
-Cabal-version:       >=1.10
-
-source-repository head
-  type: git
-  location: git://github.com/schernichkin/partial-isomorphisms.git
-
-Library
-  Hs-source-dirs:   src
-  Exposed-modules:  Control.Isomorphism.Partial
-                    Control.Isomorphism.Partial.Constructors
-                    Control.Isomorphism.Partial.Derived
-                    Control.Isomorphism.Partial.Prim
-                    Control.Isomorphism.Partial.TH
-                    Control.Isomorphism.Partial.Unsafe
-
-  default-language: Haskell2010
-  other-extensions: TemplateHaskell, KindSignatures
-  Build-depends:    base >= 3 && < 5, template-haskell >= 2.11
-
-  ghc-options:     -Wall
-
-  -- Other-modules:
-
-  -- Build-tools:
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.33.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: dd98c6799f2f9942db089c22bd9435026170a33a626f7c7910fe193e3ce54b5a
+
+name:           partial-isomorphisms
+version:        0.2.3.0
+synopsis:       Partial isomorphisms.
+description:    Partial isomorphisms as described in the paper: . Tillmann Rendel and Klaus Ostermann. Invertible Syntax Descriptions: Unifying Parsing and Pretty Printing. In /Proc. of Haskell Symposium/, 2010. . The paper also describes invertible syntax descriptions as a common interface for parsers and pretty printers. These are distributed separately in the /invertible-syntax/ package.
+category:       Control
+homepage:       http://www.informatik.uni-marburg.de/~rendel/unparse
+bug-reports:    https://github.com/schernichkin/partial-isomorphisms/issues
+author:         Tillmann Rendel <rendel@informatik.uni-marburg.de>
+maintainer:     Stanislav Chernichkin <schernichkin@gmail.com>
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    CHANGELOG.md
+
+source-repository head
+  type: git
+  location: https://github.com/schernichkin/partial-isomorphisms
+
+library
+  exposed-modules:
+      Control.Isomorphism.Partial
+      Control.Isomorphism.Partial.Constructors
+      Control.Isomorphism.Partial.Derived
+      Control.Isomorphism.Partial.Prim
+      Control.Isomorphism.Partial.TH
+      Control.Isomorphism.Partial.Unsafe
+  other-modules:
+      Paths_partial_isomorphisms
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=3 && <5
+    , template-haskell >=2.17
+  default-language: Haskell2010
diff --git a/src/Control/Isomorphism/Partial.hs b/src/Control/Isomorphism/Partial.hs
--- a/src/Control/Isomorphism/Partial.hs
+++ b/src/Control/Isomorphism/Partial.hs
@@ -1,9 +1,9 @@
-module Control.Isomorphism.Partial
-  ( module Control.Isomorphism.Partial.Prim
-  , module Control.Isomorphism.Partial.Derived
-  , module Control.Isomorphism.Partial.Constructors
-  ) where
-
-import Control.Isomorphism.Partial.Prim
-import Control.Isomorphism.Partial.Derived
+module Control.Isomorphism.Partial
+  ( module Control.Isomorphism.Partial.Prim
+  , module Control.Isomorphism.Partial.Derived
+  , module Control.Isomorphism.Partial.Constructors
+  ) where
+
+import Control.Isomorphism.Partial.Prim
+import Control.Isomorphism.Partial.Derived
 import Control.Isomorphism.Partial.Constructors
diff --git a/src/Control/Isomorphism/Partial/Constructors.hs b/src/Control/Isomorphism/Partial/Constructors.hs
--- a/src/Control/Isomorphism/Partial/Constructors.hs
+++ b/src/Control/Isomorphism/Partial/Constructors.hs
@@ -1,43 +1,43 @@
-{-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE KindSignatures  #-}
-
-module Control.Isomorphism.Partial.Constructors 
-  ( nil
-  , cons
-  , listCases
-  , left
-  , right
-  , nothing
-  , just
-  ) where
-
-import Prelude ()
-
-import Data.Either (Either (Left, Right))
-import Data.Maybe (Maybe (Just, Nothing))
-
-import Control.Isomorphism.Partial.Unsafe (Iso (Iso))
-import Control.Isomorphism.Partial.TH (defineIsomorphisms)
-
-nil :: Iso () [alpha]
-nil = Iso f g where
-  f ()  =  Just []
-  g []  =  Just ()
-  g _   =  Nothing
-
-cons :: Iso (alpha, [alpha]) [alpha]
-cons = Iso f g where
-  f (x, xs)   =  Just (x : xs)
-  g (x : xs)  =  Just (x, xs)
-  g _         =  Nothing
-
-listCases :: Iso (Either () (alpha, [alpha])) [alpha]
-listCases = Iso f g
-  where
-    f (Left ())        =  Just []
-    f (Right (x, xs))  =  Just (x : xs)
-    g []               =  Just (Left ())
-    g (x:xs)           =  Just (Right (x, xs))
-
-$(defineIsomorphisms ''Either)
-$(defineIsomorphisms ''Maybe)
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE KindSignatures  #-}
+
+module Control.Isomorphism.Partial.Constructors 
+  ( nil
+  , cons
+  , listCases
+  , left
+  , right
+  , nothing
+  , just
+  ) where
+
+import Prelude ()
+
+import Data.Either (Either (Left, Right))
+import Data.Maybe (Maybe (Just, Nothing))
+
+import Control.Isomorphism.Partial.Unsafe (Iso (Iso))
+import Control.Isomorphism.Partial.TH (defineIsomorphisms)
+
+nil :: Iso () [alpha]
+nil = Iso f g where
+  f ()  =  Just []
+  g []  =  Just ()
+  g _   =  Nothing
+
+cons :: Iso (alpha, [alpha]) [alpha]
+cons = Iso f g where
+  f (x, xs)   =  Just (x : xs)
+  g (x : xs)  =  Just (x, xs)
+  g _         =  Nothing
+
+listCases :: Iso (Either () (alpha, [alpha])) [alpha]
+listCases = Iso f g
+  where
+    f (Left ())        =  Just []
+    f (Right (x, xs))  =  Just (x : xs)
+    g []               =  Just (Left ())
+    g (x:xs)           =  Just (Right (x, xs))
+
+$(defineIsomorphisms ''Either)
+$(defineIsomorphisms ''Maybe)
diff --git a/src/Control/Isomorphism/Partial/Derived.hs b/src/Control/Isomorphism/Partial/Derived.hs
--- a/src/Control/Isomorphism/Partial/Derived.hs
+++ b/src/Control/Isomorphism/Partial/Derived.hs
@@ -1,17 +1,17 @@
-module Control.Isomorphism.Partial.Derived 
-  ( foldl
-  ) where
-
-import Prelude ()
-import Control.Category (Category (id, (.)))
-import Control.Isomorphism.Partial.Prim (Iso, inverse, unit, associate, iterate, (***))
-import Control.Isomorphism.Partial.Constructors (cons, nil)
-
-foldl :: Iso (alpha, beta) alpha -> Iso (alpha, [beta]) alpha
-foldl i = inverse unit
-        . (id *** inverse nil)
-        . iterate (step i) where
-
-  step i' = (i' *** id)
-         . associate
-         . (id *** inverse cons)
+module Control.Isomorphism.Partial.Derived 
+  ( foldl
+  ) where
+
+import Prelude ()
+import Control.Category (Category (id, (.)))
+import Control.Isomorphism.Partial.Prim (Iso, inverse, unit, associate, iterate, (***))
+import Control.Isomorphism.Partial.Constructors (cons, nil)
+
+foldl :: Iso (alpha, beta) alpha -> Iso (alpha, [beta]) alpha
+foldl i = inverse unit
+        . (id *** inverse nil)
+        . iterate (step i) where
+
+  step i' = (i' *** id)
+         . associate
+         . (id *** inverse cons)
diff --git a/src/Control/Isomorphism/Partial/Prim.hs b/src/Control/Isomorphism/Partial/Prim.hs
--- a/src/Control/Isomorphism/Partial/Prim.hs
+++ b/src/Control/Isomorphism/Partial/Prim.hs
@@ -1,122 +1,122 @@
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module Control.Isomorphism.Partial.Prim
-  ( Iso ()
-  , inverse
-  , apply
-  , unapply
-  , IsoFunctor ((<$>))
-  , ignore
-  , (***)
-  , (|||)
-  , associate
-  , commute
-  , unit
-  , element
-  , subset
-  , iterate
-  , distribute
-  ) where
-
-import Prelude ()
-
-import Control.Monad (liftM2, (>=>), fmap, mplus)
-import Control.Category (Category (id, (.)))
-
-import Data.Bool (Bool, otherwise)
-import Data.Either (Either (Left, Right))
-import Data.Eq (Eq ((==)))
-import Data.Maybe (Maybe (Just, Nothing))
-
-import Control.Isomorphism.Partial.Unsafe (Iso (Iso))
-
-inverse :: Iso alpha beta -> Iso beta alpha
-inverse (Iso f g) = Iso g f
-
-apply :: Iso alpha beta -> alpha -> Maybe beta
-apply (Iso f _) = f
-
-unapply  ::  Iso alpha beta -> beta -> Maybe alpha
-unapply  =   apply . inverse
-
-instance Category Iso where
-  g . f  =  Iso  (apply f >=> apply g)
-                 (unapply g >=> unapply f)
-  id     =  Iso  Just Just
-
-infix 5 <$>
-
-class IsoFunctor f where
-  (<$>) :: Iso alpha beta -> (f alpha -> f beta)
-
-ignore :: alpha -> Iso alpha ()
-ignore x = Iso f g where
-  f _   =  Just ()
-  g ()  =  Just x
-
--- | the product type constructor `(,)` is a bifunctor from
--- `Iso` $\times$ `Iso` to `Iso`, so that we have the
--- bifunctorial map `***` which allows two separate isomorphisms
--- to work on the two components of a tuple.
-(***) :: Iso alpha beta -> Iso gamma delta -> Iso (alpha, gamma) (beta, delta)
-i *** j = Iso f g where
-  f (a, b) = liftM2 (,) (apply i a) (apply j b)
-  g (c, d) = liftM2 (,) (unapply i c) (unapply j d)
-
--- | The mediating arrow for sums constructed with `Either`.
--- This is not a proper partial isomorphism because of `mplus`.
-(|||) :: Iso alpha gamma -> Iso beta gamma -> Iso (Either alpha beta) gamma
-i ||| j = Iso f g where
-  f (Left x) = apply i x
-  f (Right x) = apply j x
-  g y = (Left `fmap` unapply i y) `mplus` (Right `fmap` unapply j y)
-
-
--- | Nested products associate.
-associate :: Iso (alpha, (beta, gamma)) ((alpha, beta), gamma)
-associate = Iso f g where
-  f (a, (b, c)) = Just ((a, b), c)
-  g ((a, b), c) = Just (a, (b, c))
-
--- | Products commute.
-commute :: Iso (alpha, beta) (beta, alpha)
-commute = Iso f f where
-  f (a, b) = Just (b, a)
-
--- | `()` is the unit element for products.
-unit :: Iso alpha (alpha, ())
-unit = Iso f g where
-  f a = Just (a, ())
-  g (a, ()) = Just a
-
--- | Products distribute over sums.
-distribute  ::  Iso (alpha, Either beta gamma) (Either (alpha, beta) (alpha, gamma))
-distribute  =   Iso f g where
-  f (a, Left   b)    =  Just (Left   (a, b))
-  f (a, Right  c)    =  Just (Right  (a, c))
-  g (Left   (a, b))  =  Just (a,  Left   b)
-  g (Right  (a, b))  =  Just (a,  Right  b)
-
--- | `element x` is the partial isomorphism between `()` and the
--- singleton set which contains just `x`.
-element :: Eq alpha => alpha -> Iso () alpha
-element x = Iso
-  (\_ -> Just x)
-  (\b -> if x == b then Just () else Nothing)
-
--- | For a predicate `p`, `subset p` is the identity isomorphism
--- restricted to elements matching the predicate.
-subset :: (alpha -> Bool) -> Iso alpha alpha
-subset p = Iso f f where
-  f x | p x = Just x | otherwise = Nothing
-
-iterate :: Iso alpha alpha -> Iso alpha alpha
-iterate step = Iso f g where
-  f = Just . driver (apply step)
-  g = Just . driver (unapply step)
-
-  driver :: (alpha -> Maybe alpha) -> (alpha -> alpha)
-  driver step' state
-    =  case step' state of
-         Just state'  ->  driver step' state'
-         Nothing      ->  state
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Control.Isomorphism.Partial.Prim
+  ( Iso ()
+  , inverse
+  , apply
+  , unapply
+  , IsoFunctor ((<$>))
+  , ignore
+  , (***)
+  , (|||)
+  , associate
+  , commute
+  , unit
+  , element
+  , subset
+  , iterate
+  , distribute
+  ) where
+
+import Prelude ()
+
+import Control.Monad (liftM2, (>=>), fmap, mplus)
+import Control.Category (Category (id, (.)))
+
+import Data.Bool (Bool, otherwise)
+import Data.Either (Either (Left, Right))
+import Data.Eq (Eq ((==)))
+import Data.Maybe (Maybe (Just, Nothing))
+
+import Control.Isomorphism.Partial.Unsafe (Iso (Iso))
+
+inverse :: Iso alpha beta -> Iso beta alpha
+inverse (Iso f g) = Iso g f
+
+apply :: Iso alpha beta -> alpha -> Maybe beta
+apply (Iso f _) = f
+
+unapply  ::  Iso alpha beta -> beta -> Maybe alpha
+unapply  =   apply . inverse
+
+instance Category Iso where
+  g . f  =  Iso  (apply f >=> apply g)
+                 (unapply g >=> unapply f)
+  id     =  Iso  Just Just
+
+infix 5 <$>
+
+class IsoFunctor f where
+  (<$>) :: Iso alpha beta -> (f alpha -> f beta)
+
+ignore :: alpha -> Iso alpha ()
+ignore x = Iso f g where
+  f _   =  Just ()
+  g ()  =  Just x
+
+-- | the product type constructor `(,)` is a bifunctor from
+-- `Iso` $\times$ `Iso` to `Iso`, so that we have the
+-- bifunctorial map `***` which allows two separate isomorphisms
+-- to work on the two components of a tuple.
+(***) :: Iso alpha beta -> Iso gamma delta -> Iso (alpha, gamma) (beta, delta)
+i *** j = Iso f g where
+  f (a, b) = liftM2 (,) (apply i a) (apply j b)
+  g (c, d) = liftM2 (,) (unapply i c) (unapply j d)
+
+-- | The mediating arrow for sums constructed with `Either`.
+-- This is not a proper partial isomorphism because of `mplus`.
+(|||) :: Iso alpha gamma -> Iso beta gamma -> Iso (Either alpha beta) gamma
+i ||| j = Iso f g where
+  f (Left x) = apply i x
+  f (Right x) = apply j x
+  g y = (Left `fmap` unapply i y) `mplus` (Right `fmap` unapply j y)
+
+
+-- | Nested products associate.
+associate :: Iso (alpha, (beta, gamma)) ((alpha, beta), gamma)
+associate = Iso f g where
+  f (a, (b, c)) = Just ((a, b), c)
+  g ((a, b), c) = Just (a, (b, c))
+
+-- | Products commute.
+commute :: Iso (alpha, beta) (beta, alpha)
+commute = Iso f f where
+  f (a, b) = Just (b, a)
+
+-- | `()` is the unit element for products.
+unit :: Iso alpha (alpha, ())
+unit = Iso f g where
+  f a = Just (a, ())
+  g (a, ()) = Just a
+
+-- | Products distribute over sums.
+distribute  ::  Iso (alpha, Either beta gamma) (Either (alpha, beta) (alpha, gamma))
+distribute  =   Iso f g where
+  f (a, Left   b)    =  Just (Left   (a, b))
+  f (a, Right  c)    =  Just (Right  (a, c))
+  g (Left   (a, b))  =  Just (a,  Left   b)
+  g (Right  (a, b))  =  Just (a,  Right  b)
+
+-- | `element x` is the partial isomorphism between `()` and the
+-- singleton set which contains just `x`.
+element :: Eq alpha => alpha -> Iso () alpha
+element x = Iso
+  (\_ -> Just x)
+  (\b -> if x == b then Just () else Nothing)
+
+-- | For a predicate `p`, `subset p` is the identity isomorphism
+-- restricted to elements matching the predicate.
+subset :: (alpha -> Bool) -> Iso alpha alpha
+subset p = Iso f f where
+  f x | p x = Just x | otherwise = Nothing
+
+iterate :: Iso alpha alpha -> Iso alpha alpha
+iterate step = Iso f g where
+  f = Just . driver (apply step)
+  g = Just . driver (unapply step)
+
+  driver :: (alpha -> Maybe alpha) -> (alpha -> alpha)
+  driver step' state
+    =  case step' state of
+         Just state'  ->  driver step' state'
+         Nothing      ->  state
diff --git a/src/Control/Isomorphism/Partial/TH.hs b/src/Control/Isomorphism/Partial/TH.hs
--- a/src/Control/Isomorphism/Partial/TH.hs
+++ b/src/Control/Isomorphism/Partial/TH.hs
@@ -1,132 +1,135 @@
-{-# LANGUAGE TemplateHaskell #-}
-module Control.Isomorphism.Partial.TH
-  ( constructorIso
-  , defineIsomorphisms
-  ) where
-
-import           Control.Monad
-import           Data.Char                          (toLower)
-import           Data.List                          (find)
-import           Language.Haskell.TH
-
-import           Control.Isomorphism.Partial.Unsafe (Iso (Iso))
-
-gadtError :: a
-gadtError = error "Control.Isomorphism.Partial.TH: GADTs currently not supported."
-{-# NOINLINE gadtError #-}
-
--- | Extract the name of a constructor, e.g. ":" or "Just".
-conName :: Con -> Name
-conName (NormalC name _)   =   name
-conName (RecC name _)      =   name
-conName (InfixC _ name _)  =   name
-conName (ForallC _ _ con)  =   conName con
-conName (GadtC _ _ _)      =   gadtError
-conName (RecGadtC _ _ _)   =   gadtError
-
--- | Extract the types of the constructor's fields.
-conFields :: Con -> [Type]
-conFields (NormalC _ fields)  =   map (\(_, t) -> t) fields
-conFields (RecC _ fields)     =   map (\(_, _, t) -> t) fields
-conFields (InfixC lhs _ rhs)  =   map (\(_, t) -> t) [lhs, rhs]
-conFields (ForallC _ _ con)   =   conFields con
-conFields (GadtC _ _ _)       =   gadtError
-conFields (RecGadtC _ _ _)    =   gadtError
-
--- Data dec information
-data DecInfo = DecInfo Type [TyVarBndr] [Con]
-
--- | Extract data or newtype declaration information
-decInfo :: Dec -> Q DecInfo
-decInfo (DataD    _ name tyVars _ cs _) =  return $ DecInfo (ConT name) tyVars cs
-decInfo (NewtypeD _ name tyVars _ c _) =  return $ DecInfo (ConT name) tyVars [c]
-decInfo _ = fail "partial isomorphisms can only be derived for constructors of data type or newtype declarations."
-
--- | Convert tyVarBndr to type
-tyVarBndrToType :: TyVarBndr -> Type
-tyVarBndrToType (PlainTV  n)   = VarT n
-tyVarBndrToType (KindedTV n k) = SigT (VarT n) k
-
--- | Create Iso type for specified type and conctructor fields (Iso (a, b) (CustomType a b c))
-isoType :: Type -> [TyVarBndr] -> [Type] -> Q Type
-isoType typ tyVarBndrs fields = do
-    isoCon <- [t| Iso |]
-    return $ ForallT tyVarBndrs [] $ isoCon `AppT` (isoArgs fields) `AppT` (applyAll typ $ map tyVarBndrToType tyVarBndrs)
-
-isoArgs :: [Type] -> Type
-isoArgs []     = TupleT 0
-isoArgs [x]    = x
-isoArgs (x:xs) = AppT (AppT (TupleT 2) x) (isoArgs xs)
-
--- | Apply all types to supplied type
-applyAll :: Type -> [Type] -> Type
-applyAll = foldl AppT
-
--- | Construct a partial isomorphism expression for a constructor,
--- given the constructor's name.
-constructorIso :: Name -> ExpQ
-constructorIso name = do
-  DataConI n _ d    <-  reify name
-  TyConI dec        <-  reify d
-  DecInfo _ _ cs    <-  decInfo dec
-  let Just con      =   find (\c -> n == conName c) cs
-  isoFromCon (wildcard cs) con
-
-wildcard :: [Con] -> [MatchQ]
-wildcard cs
-  =  if length cs > 1
-     then  [match (wildP) (normalB [| Nothing |]) []]
-     else  []
-
--- | Converts a constructor name (starting with an upper-case
---   letter) into a function name (starting with a lower-case
---   letter).
-rename :: Name -> Name
-rename n
-  = mkName (toLower c : cs) where c : cs = nameBase n
-
-defineIsomorphisms :: Name -> Q [Dec]
-defineIsomorphisms d = do
-  TyConI dec  <-  reify d
-  DecInfo typ tyVarBndrs cs          <-  decInfo dec
-  join `fmap` mapM (\a -> defFromCon (wildcard cs) typ tyVarBndrs a) cs
-
--- | Constructs a partial isomorphism definition for a
---   constructor, given information about the constructor.
---   The name of the partial isomorphisms is constructed by
---   spelling the constructor name with an initial lower-case
---   letter.
-defFromCon :: [MatchQ] -> Type -> [TyVarBndr] -> Con -> DecsQ
-defFromCon matches t tyVarBndrs con = do
-    let funName = rename $ conName con
-    sig <- SigD funName `fmap` isoType t tyVarBndrs (conFields con)
-    fun <- funD funName [ clause [] (normalB (isoFromCon matches con)) [] ]
-    return [sig, fun]
-
--- | Constructs a partial isomorphism expression for a
---   constructor, given information about the constructor.
-isoFromCon :: [MatchQ] -> Con -> ExpQ
-isoFromCon matches con = do
-  let c     =   conName con
-  let fs    =   conFields con
-  let n     =   length fs
-  (ps, vs)  <-  genPE n
-  v         <-  newName "x"
-  let f     =   lamE [nested tupP ps]
-                  [| Just $(foldl appE (conE c) vs) |]
-  let g     =   lamE [varP v]
-                  (caseE (varE v) $
-                    [ match (conP c ps)
-                        (normalB [| Just $(nested tupE vs) |]) []
-                    ] ++ matches)
-  [| Iso $f $g |]
-
-genPE :: Int -> Q ([PatQ], [ExpQ])
-genPE n = do
-  ids <- replicateM n (newName "x")
-  return (map varP ids, map varE ids)
-
-nested :: ([t] -> t) -> [t] -> t
-nested tup []      =  tup []
-nested _   [x]     =  x
-nested tup (x:xs)  =  tup [x, nested tup xs]
+{-# LANGUAGE TemplateHaskell, DataKinds #-}
+module Control.Isomorphism.Partial.TH
+  ( constructorIso
+  , defineIsomorphisms
+  ) where
+
+import           Control.Monad
+import           Data.Char                          (toLower)
+import           Data.List                          (find)
+import           Language.Haskell.TH
+
+import           Control.Isomorphism.Partial.Unsafe (Iso (Iso))
+
+gadtError :: a
+gadtError = error "Control.Isomorphism.Partial.TH: GADTs currently not supported."
+{-# NOINLINE gadtError #-}
+
+-- | Extract the name of a constructor, e.g. ":" or "Just".
+conName :: Con -> Name
+conName (NormalC name _)   =   name
+conName (RecC name _)      =   name
+conName (InfixC _ name _)  =   name
+conName (ForallC _ _ con)  =   conName con
+conName (GadtC _ _ _)      =   gadtError
+conName (RecGadtC _ _ _)   =   gadtError
+
+-- | Extract the types of the constructor's fields.
+conFields :: Con -> [Type]
+conFields (NormalC _ fields)  =   map (\(_, t) -> t) fields
+conFields (RecC _ fields)     =   map (\(_, _, t) -> t) fields
+conFields (InfixC lhs _ rhs)  =   map (\(_, t) -> t) [lhs, rhs]
+conFields (ForallC _ _ con)   =   conFields con
+conFields (GadtC _ _ _)       =   gadtError
+conFields (RecGadtC _ _ _)    =   gadtError
+
+-- Data dec information
+data DecInfo flag = DecInfo Type [TyVarBndr flag] [Con]
+
+-- | Extract data or newtype declaration information
+decInfo :: Dec -> Q (DecInfo ())
+decInfo (DataD    _ name tyVars _ cs _) =  return $ DecInfo (ConT name) tyVars cs
+decInfo (NewtypeD _ name tyVars _ c _) =  return $ DecInfo (ConT name) tyVars [c]
+decInfo _ = fail "partial isomorphisms can only be derived for constructors of data type or newtype declarations."
+
+-- | Convert tyVarBndr to type
+tyVarBndrToType :: TyVarBndr () -> Type
+tyVarBndrToType (PlainTV n _)   = VarT n
+tyVarBndrToType (KindedTV n _ k) = SigT (VarT n) k
+
+-- | Create Iso type for specified type and conctructor fields (Iso (a, b) (CustomType a b c))
+isoType :: Type -> [TyVarBndr ()] -> [Type] -> Q Type
+isoType typ tyVarBndrs fields = do
+    isoCon <- [t| Iso |]
+    return $ ForallT (map specified tyVarBndrs) [] $ isoCon `AppT` (isoArgs fields) `AppT` (applyAll typ $ map tyVarBndrToType tyVarBndrs)
+    where
+      specified (PlainTV name _) = PlainTV name SpecifiedSpec
+      specified (KindedTV name _ kind) = KindedTV name SpecifiedSpec kind
+
+isoArgs :: [Type] -> Type
+isoArgs []     = TupleT 0
+isoArgs [x]    = x
+isoArgs (x:xs) = AppT (AppT (TupleT 2) x) (isoArgs xs)
+
+-- | Apply all types to supplied type
+applyAll :: Type -> [Type] -> Type
+applyAll = foldl AppT
+
+-- | Construct a partial isomorphism expression for a constructor,
+-- given the constructor's name.
+constructorIso :: Name -> ExpQ
+constructorIso name = do
+  DataConI n _ d    <-  reify name
+  TyConI dec        <-  reify d
+  DecInfo _ _ cs    <-  decInfo dec
+  let Just con      =   find (\c -> n == conName c) cs
+  isoFromCon (wildcard cs) con
+
+wildcard :: [Con] -> [MatchQ]
+wildcard cs
+  =  if length cs > 1
+     then  [match (wildP) (normalB [| Nothing |]) []]
+     else  []
+
+-- | Converts a constructor name (starting with an upper-case
+--   letter) into a function name (starting with a lower-case
+--   letter).
+rename :: Name -> Name
+rename n
+  = mkName (toLower c : cs) where c : cs = nameBase n
+
+defineIsomorphisms :: Name -> Q [Dec]
+defineIsomorphisms d = do
+  TyConI dec  <-  reify d
+  DecInfo typ tyVarBndrs cs          <-  decInfo dec
+  join `fmap` mapM (\a -> defFromCon (wildcard cs) typ tyVarBndrs a) cs
+
+-- | Constructs a partial isomorphism definition for a
+--   constructor, given information about the constructor.
+--   The name of the partial isomorphisms is constructed by
+--   spelling the constructor name with an initial lower-case
+--   letter.
+defFromCon :: [MatchQ] -> Type -> [TyVarBndr ()] -> Con -> DecsQ
+defFromCon matches t tyVarBndrs con = do
+    let funName = rename $ conName con
+    sig <- SigD funName `fmap` isoType t tyVarBndrs (conFields con)
+    fun <- funD funName [ clause [] (normalB (isoFromCon matches con)) [] ]
+    return [sig, fun]
+
+-- | Constructs a partial isomorphism expression for a
+--   constructor, given information about the constructor.
+isoFromCon :: [MatchQ] -> Con -> ExpQ
+isoFromCon matches con = do
+  let c     =   conName con
+  let fs    =   conFields con
+  let n     =   length fs
+  (ps, vs)  <-  genPE n
+  v         <-  newName "x"
+  let f     =   lamE [nested tupP ps]
+                  [| Just $(foldl appE (conE c) vs) |]
+  let g     =   lamE [varP v]
+                  (caseE (varE v) $
+                    [ match (conP c ps)
+                        (normalB [| Just $(nested tupE vs) |]) []
+                    ] ++ matches)
+  [| Iso $f $g |]
+
+genPE :: Int -> Q ([PatQ], [ExpQ])
+genPE n = do
+  ids <- replicateM n (newName "x")
+  return (map varP ids, map varE ids)
+
+nested :: ([t] -> t) -> [t] -> t
+nested tup []      =  tup []
+nested _   [x]     =  x
+nested tup (x:xs)  =  tup [x, nested tup xs]
diff --git a/src/Control/Isomorphism/Partial/Unsafe.hs b/src/Control/Isomorphism/Partial/Unsafe.hs
--- a/src/Control/Isomorphism/Partial/Unsafe.hs
+++ b/src/Control/Isomorphism/Partial/Unsafe.hs
@@ -1,9 +1,9 @@
-module Control.Isomorphism.Partial.Unsafe
-  ( Iso (Iso)
-  ) where
-
-import Prelude ()
-import Data.Maybe (Maybe ())
-
-data Iso alpha beta 
-  = Iso (alpha -> Maybe beta) (beta -> Maybe alpha)
+module Control.Isomorphism.Partial.Unsafe
+  ( Iso (Iso)
+  ) where
+
+import Prelude ()
+import Data.Maybe (Maybe ())
+
+data Iso alpha beta 
+  = Iso (alpha -> Maybe beta) (beta -> Maybe alpha)
