diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,5 +1,28 @@
 # dhall-to-cabal change log
 
+## Next -- [YYYY-MM-DD]
+
+## 1.3.4.0 -- 2019-07-05
+
+* Add compatibility for `optparse-applicative-0.15`.
+
+* Remove `dhall/types/CustomSetup.dhall` in favour of the identical
+  `dhall/types/SetupBuildInfo.dhall`.
+
+* `cabal-to-dhall` no longer generates bogus output for an unknown
+  kind of `source-repository` stanza.
+
+* Added `dhall/types/ForeignLibOption.dhall`, also available as
+  `types.ForeignLibOption`, and `--print-type ForeignLibOption`.
+  Likewise `ForeignLibType`.
+
+* Support `prettyprinter` 1.3.
+
+* Teach `--print-type` about `Dependency`, `SetupBuildInfo`, `TestType`,
+  `Mixin` and `Flag`.
+
+* Use `dhall` version 1.24.
+
 ## 1.3.3.0 -- 2019-05-15
 
 * All constructors that previously took an empty record now use the
diff --git a/dhall-to-cabal.cabal b/dhall-to-cabal.cabal
--- a/dhall-to-cabal.cabal
+++ b/dhall-to-cabal.cabal
@@ -9,7 +9,7 @@
 -- 'dhall-to-cabal -- dhall-to-cabal.dhall'.
 -- * * * * * * * * * * * * WARNING * * * * * * * * * * * *
 name: dhall-to-cabal
-version: 1.3.3.0
+version: 1.3.4.0
 license: MIT
 license-file: LICENSE
 maintainer: ollie@ocharles.org.uk
@@ -43,6 +43,7 @@
         CabalToDhall
         DhallLocation
         DhallToCabal
+        DhallToCabal.FactorType
         DhallToCabal.Util
     hs-source-dirs: lib
     other-modules:
@@ -66,8 +67,9 @@
         bytestring ^>=0.10,
         containers ^>=0.5 || ^>=0.6,
         contravariant ^>=1.4 || ^>=1.5,
-        dhall ^>=1.23.0,
+        dhall ^>=1.24.0,
         filepath ^>=1.4,
+        microlens ^>=0.1.0.0 || ^>=0.2.0.0 || ^>=0.3.0.0 || ^>=0.4.0.0,
         text ^>=1.2,
         transformers ^>=0.5.2,
         vector ^>=0.12
@@ -89,13 +91,14 @@
     build-depends:
         Cabal ^>=2.4,
         base ^>=4.10 || ^>=4.11 || ^>=4.12,
-        dhall ^>=1.23.0,
+        containers ^>=0.5 || ^>=0.6,
+        dhall ^>=1.24.0,
         dhall-to-cabal -any,
         directory ^>=1.3.0.2,
         filepath ^>=1.4,
         microlens ^>=0.1.0.0 || ^>=0.2.0.0 || ^>=0.3.0.0 || ^>=0.4.0.0,
-        optparse-applicative ^>=0.13.2 || ^>=0.14,
-        prettyprinter ^>=1.2.0.1,
+        optparse-applicative ^>=0.13.2 || ^>=0.14 || ^>=0.15,
+        prettyprinter ^>=1.2.0.1 || ^>=1.3.0,
         text ^>=1.2,
         transformers ^>=0.5.2
 
@@ -115,11 +118,11 @@
                  -fno-warn-name-shadowing
     build-depends:
         base ^>=4.10 || ^>=4.11 || ^>=4.12,
-        dhall ^>=1.23.0,
+        dhall ^>=1.24.0,
         bytestring ^>=0.10,
         dhall-to-cabal -any,
-        optparse-applicative ^>=0.13.2 || ^>=0.14,
-        prettyprinter ^>=1.2.0.1,
+        optparse-applicative ^>=0.13.2 || ^>=0.14 || ^>=0.15,
+        prettyprinter ^>=1.2.0.1 || ^>=1.3.0,
         text ^>=1.2
 
 executable dhall-to-cabal-meta
@@ -135,11 +138,11 @@
     build-depends:
         base ^>=4.10 || ^>=4.11 || ^>=4.12,
         directory ^>=1.3.0.2,
-        dhall ^>=1.23.0,
+        dhall ^>=1.24.0,
         dhall-to-cabal -any,
         filepath ^>=1.4,
-        optparse-applicative ^>=0.13.2 || ^>=0.14,
-        prettyprinter ^>=1.2.0.1
+        optparse-applicative ^>=0.13.2 || ^>=0.14 || ^>=0.15,
+        prettyprinter ^>=1.2.0.1 || ^>=1.3.0
 
 test-suite golden-tests
     type: exitcode-stdio-1.0
@@ -156,11 +159,11 @@
         Cabal ^>=2.4,
         Diff ^>=0.3.4,
         bytestring ^>=0.10,
-        dhall ^>=1.23.0,
+        dhall ^>=1.24.0,
         dhall-to-cabal -any,
         filepath ^>=1.4,
         microlens ^>=0.1.0.0 || ^>=0.2.0.0 || ^>=0.3.0.0 || ^>=0.4.0.0,
-        prettyprinter ^>=1.2.0.1,
+        prettyprinter ^>=1.2.0.1 || ^>=1.3.0,
         tasty ^>=0.11 || ^>=0.12 || ^>=1.0 || ^>=1.1 || ^>=1.2,
         tasty-golden ^>=2.3,
         text ^>=1.2
@@ -180,7 +183,7 @@
     build-depends:
         base ^>=4.10 || ^>=4.11 || ^>=4.12,
         Cabal ^>=2.4,
-        dhall ^>=1.23.0,
+        dhall ^>=1.24.0,
         dhall-to-cabal -any,
         tasty ^>=0.11 || ^>=0.12 || ^>=1.0 || ^>=1.1 || ^>=1.2,
         tasty-hunit ^>=0.10.0.1,
diff --git a/dhall/defaults/Benchmark.dhall b/dhall/defaults/Benchmark.dhall
--- a/dhall/defaults/Benchmark.dhall
+++ b/dhall/defaults/Benchmark.dhall
@@ -1,2 +1,2 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall in ././BuildInfo.dhall
+./BuildInfo.dhall
diff --git a/dhall/defaults/BuildInfo.dhall b/dhall/defaults/BuildInfo.dhall
--- a/dhall/defaults/BuildInfo.dhall
+++ b/dhall/defaults/BuildInfo.dhall
@@ -1,5 +1,5 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall
+let types = ../types.dhall
 
 in  { autogen-modules =
         [] : List Text
@@ -17,7 +17,7 @@
     , cc-options =
         [] : List Text
     , compiler-options =
-        ././CompilerOptions.dhall
+        ./CompilerOptions.dhall
     , cpp-options =
         [] : List Text
     , default-extensions =
@@ -55,11 +55,11 @@
     , pkgconfig-depends =
         [] : List { name : Text, version : types.VersionRange }
     , profiling-options =
-        ././CompilerOptions.dhall
+        ./CompilerOptions.dhall
     , shared-options =
-        ././CompilerOptions.dhall
+        ./CompilerOptions.dhall
     , static-options =
-        ././CompilerOptions.dhall
+        ./CompilerOptions.dhall
     , mixins =
         [] : List types.Mixin
     , asm-options =
diff --git a/dhall/defaults/CompilerOptions.dhall b/dhall/defaults/CompilerOptions.dhall
--- a/dhall/defaults/CompilerOptions.dhall
+++ b/dhall/defaults/CompilerOptions.dhall
@@ -1,26 +1,24 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall
-
-in  { Eta =
-        [] : List Text
-    , GHC =
-        [] : List Text
-    , GHCJS =
-        [] : List Text
-    , HBC =
-        [] : List Text
-    , Helium =
-        [] : List Text
-    , Hugs =
-        [] : List Text
-    , JHC =
-        [] : List Text
-    , LHC =
-        [] : List Text
-    , NHC =
-        [] : List Text
-    , UHC =
-        [] : List Text
-    , YHC =
-        [] : List Text
-    }
+{ Eta =
+    [] : List Text
+, GHC =
+    [] : List Text
+, GHCJS =
+    [] : List Text
+, HBC =
+    [] : List Text
+, Helium =
+    [] : List Text
+, Hugs =
+    [] : List Text
+, JHC =
+    [] : List Text
+, LHC =
+    [] : List Text
+, NHC =
+    [] : List Text
+, UHC =
+    [] : List Text
+, YHC =
+    [] : List Text
+}
diff --git a/dhall/defaults/Executable.dhall b/dhall/defaults/Executable.dhall
--- a/dhall/defaults/Executable.dhall
+++ b/dhall/defaults/Executable.dhall
@@ -1,4 +1,2 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall
-
-in  ././BuildInfo.dhall ⫽ { scope = types.Scope.Public }
+let types = ../types.dhall in ./BuildInfo.dhall ⫽ { scope = types.Scope.Public }
diff --git a/dhall/defaults/Library.dhall b/dhall/defaults/Library.dhall
--- a/dhall/defaults/Library.dhall
+++ b/dhall/defaults/Library.dhall
@@ -1,16 +1,10 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall
-
-in    ././BuildInfo.dhall
-    ⫽ { exposed-modules =
-          [] : List Text
-      , reexported-modules =
-          [] : List
-               { name :
-                   Text
-               , original :
-                   { name : Text, package : Optional Text }
-               }
-      , signatures =
-          [] : List Text
-      }
+  ./BuildInfo.dhall
+⫽ { exposed-modules =
+      [] : List Text
+  , reexported-modules =
+      [] : List
+           { name : Text, original : { name : Text, package : Optional Text } }
+  , signatures =
+      [] : List Text
+  }
diff --git a/dhall/defaults/Package.dhall b/dhall/defaults/Package.dhall
--- a/dhall/defaults/Package.dhall
+++ b/dhall/defaults/Package.dhall
@@ -1,5 +1,5 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall
+let types = ../types.dhall
 
 in  { author =
         ""
@@ -18,7 +18,7 @@
     , build-type =
         None types.BuildType
     , cabal-version =
-        ./../Version/v.dhall "2.2"
+        ../Version/v.dhall "2.2"
     , category =
         ""
     , copyright =
@@ -82,5 +82,5 @@
     , x-fields =
         [] : List { _1 : Text, _2 : Text }
     , custom-setup =
-        None types.CustomSetup
+        None types.SetupBuildInfo
     }
diff --git a/dhall/defaults/SourceRepo.dhall b/dhall/defaults/SourceRepo.dhall
--- a/dhall/defaults/SourceRepo.dhall
+++ b/dhall/defaults/SourceRepo.dhall
@@ -1,5 +1,5 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall
+let types = ../types.dhall
 
 in  { type =
         None types.RepoType
diff --git a/dhall/defaults/TestSuite.dhall b/dhall/defaults/TestSuite.dhall
--- a/dhall/defaults/TestSuite.dhall
+++ b/dhall/defaults/TestSuite.dhall
@@ -1,2 +1,2 @@
 -- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
-let types = ./../types.dhall in ././BuildInfo.dhall
+./BuildInfo.dhall
diff --git a/dhall/types.dhall b/dhall/types.dhall
--- a/dhall/types.dhall
+++ b/dhall/types.dhall
@@ -10,8 +10,6 @@
     ./types/CompilerOptions.dhall
 , Config =
     ./types/Config.dhall
-, CustomSetup =
-    ./types/CustomSetup.dhall
 , Dependency =
     ./types/Dependency.dhall
 , Executable =
@@ -22,6 +20,10 @@
     ./types/Extension.dhall
 , Flag =
     ./types/Flag.dhall
+, ForeignLibOption =
+    ./types/ForeignLibOption.dhall
+, ForeignLibType =
+    ./types/ForeignLibType.dhall
 , ForeignLibrary =
     ./types/ForeignLibrary.dhall
 , Language =
diff --git a/dhall/types/Arch.dhall b/dhall/types/Arch.dhall
--- a/dhall/types/Arch.dhall
+++ b/dhall/types/Arch.dhall
@@ -1,3 +1,4 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < AArch64
 | Alpha
 | Arm
diff --git a/dhall/types/Benchmark.dhall b/dhall/types/Benchmark.dhall
--- a/dhall/types/Benchmark.dhall
+++ b/dhall/types/Benchmark.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 ./BuildInfo.dhall ⩓ { main-is : Text }
diff --git a/dhall/types/BuildInfo.dhall b/dhall/types/BuildInfo.dhall
--- a/dhall/types/BuildInfo.dhall
+++ b/dhall/types/BuildInfo.dhall
@@ -1,9 +1,14 @@
-{ autogen-modules :
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+{ asm-options :
     List Text
+, asm-sources :
+    List Text
+, autogen-modules :
+    List Text
 , build-depends :
     List ./Dependency.dhall
 , build-tool-depends :
-    List { component : Text, package : Text, version : ./VersionRange.dhall }
+    List { package : Text, component : Text, version : ./VersionRange.dhall }
 , build-tools :
     List { exe : Text, version : ./VersionRange.dhall }
 , buildable :
@@ -12,36 +17,50 @@
     List Text
 , cc-options :
     List Text
+, cmm-options :
+    List Text
+, cmm-sources :
+    List Text
 , compiler-options :
     ./CompilerOptions.dhall
 , cpp-options :
     List Text
+, cxx-options :
+    List Text
+, cxx-sources :
+    List Text
 , default-extensions :
     List ./Extension.dhall
 , default-language :
     Optional ./Language.dhall
+, extra-bundled-libs :
+    List Text
 , extra-framework-dirs :
     List Text
 , extra-ghci-libraries :
     List Text
 , extra-lib-dirs :
     List Text
+, extra-lib-flavours :
+    List Text
 , extra-libraries :
     List Text
 , frameworks :
     List Text
 , hs-source-dirs :
     List Text
-, includes :
-    List Text
 , include-dirs :
     List Text
+, includes :
+    List Text
 , install-includes :
     List Text
 , js-sources :
     List Text
 , ld-options :
     List Text
+, mixins :
+    List ./Mixin.dhall
 , other-extensions :
     List ./Extension.dhall
 , other-languages :
@@ -56,24 +75,6 @@
     ./CompilerOptions.dhall
 , static-options :
     ./CompilerOptions.dhall
-, mixins :
-    List ./Mixin.dhall
-, asm-options :
-    List Text
-, asm-sources :
-    List Text
-, cmm-options :
-    List Text
-, cmm-sources :
-    List Text
-, cxx-options :
-    List Text
-, cxx-sources :
-    List Text
 , virtual-modules :
-    List Text
-, extra-lib-flavours :
-    List Text
-, extra-bundled-libs :
     List Text
 }
diff --git a/dhall/types/BuildType.dhall b/dhall/types/BuildType.dhall
--- a/dhall/types/BuildType.dhall
+++ b/dhall/types/BuildType.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < Configure | Custom | Make | Simple >
diff --git a/dhall/types/Compiler.dhall b/dhall/types/Compiler.dhall
--- a/dhall/types/Compiler.dhall
+++ b/dhall/types/Compiler.dhall
@@ -1,3 +1,4 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < Eta
 | GHC
 | GHCJS
diff --git a/dhall/types/CompilerOptions.dhall b/dhall/types/CompilerOptions.dhall
--- a/dhall/types/CompilerOptions.dhall
+++ b/dhall/types/CompilerOptions.dhall
@@ -1,23 +1,24 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 { Eta :
     List Text
 , GHC :
     List Text
 , GHCJS :
     List Text
-, NHC :
-    List Text
-, YHC :
-    List Text
-, Hugs :
-    List Text
 , HBC :
     List Text
 , Helium :
     List Text
+, Hugs :
+    List Text
 , JHC :
     List Text
 , LHC :
     List Text
+, NHC :
+    List Text
 , UHC :
+    List Text
+, YHC :
     List Text
 }
diff --git a/dhall/types/Config.dhall b/dhall/types/Config.dhall
--- a/dhall/types/Config.dhall
+++ b/dhall/types/Config.dhall
@@ -1,9 +1,10 @@
-{ os :
-    ./OS.dhall → Bool
-, arch :
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+{ arch :
     ./Arch.dhall → Bool
-, impl :
-    ./Compiler.dhall → ./VersionRange.dhall → Bool
 , flag :
     Text → Bool
+, impl :
+    ./Compiler.dhall → ./VersionRange.dhall → Bool
+, os :
+    ./OS.dhall → Bool
 }
diff --git a/dhall/types/CustomSetup.dhall b/dhall/types/CustomSetup.dhall
deleted file mode 100644
--- a/dhall/types/CustomSetup.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-{ setup-depends : List ./Dependency.dhall }
diff --git a/dhall/types/Dependency.dhall b/dhall/types/Dependency.dhall
--- a/dhall/types/Dependency.dhall
+++ b/dhall/types/Dependency.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 { bounds : ./VersionRange.dhall, package : Text }
diff --git a/dhall/types/Executable.dhall b/dhall/types/Executable.dhall
--- a/dhall/types/Executable.dhall
+++ b/dhall/types/Executable.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 ./BuildInfo.dhall ⩓ { main-is : Text, scope : ./Scope.dhall }
diff --git a/dhall/types/Extension.dhall b/dhall/types/Extension.dhall
--- a/dhall/types/Extension.dhall
+++ b/dhall/types/Extension.dhall
@@ -1,3 +1,4 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < AllowAmbiguousTypes :
     Bool
 | ApplicativeDo :
diff --git a/dhall/types/Flag.dhall b/dhall/types/Flag.dhall
--- a/dhall/types/Flag.dhall
+++ b/dhall/types/Flag.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 { default : Bool, description : Text, manual : Bool, name : Text }
diff --git a/dhall/types/ForeignLibOption.dhall b/dhall/types/ForeignLibOption.dhall
new file mode 100644
--- /dev/null
+++ b/dhall/types/ForeignLibOption.dhall
@@ -0,0 +1,1 @@
+< Standalone >
diff --git a/dhall/types/ForeignLibType.dhall b/dhall/types/ForeignLibType.dhall
new file mode 100644
--- /dev/null
+++ b/dhall/types/ForeignLibType.dhall
@@ -0,0 +1,1 @@
+< Shared | Static >
diff --git a/dhall/types/ForeignLibrary.dhall b/dhall/types/ForeignLibrary.dhall
--- a/dhall/types/ForeignLibrary.dhall
+++ b/dhall/types/ForeignLibrary.dhall
@@ -1,8 +1,9 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
   ./BuildInfo.dhall
 ⩓ { type :
-      < Shared | Static >
+      ./ForeignLibType.dhall
   , options :
-      List < Standalone >
+      List ./ForeignLibOption.dhall
   , lib-version-info :
       Optional { current : Natural, revision : Natural, age : Natural }
   , lib-version-linux :
diff --git a/dhall/types/Language.dhall b/dhall/types/Language.dhall
--- a/dhall/types/Language.dhall
+++ b/dhall/types/Language.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < Haskell2010 | Haskell98 | UnknownLanguage : { _1 : Text } >
diff --git a/dhall/types/Library.dhall b/dhall/types/Library.dhall
--- a/dhall/types/Library.dhall
+++ b/dhall/types/Library.dhall
@@ -1,8 +1,9 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
   ./BuildInfo.dhall
 ⩓ { exposed-modules :
       List Text
   , reexported-modules :
-      List { name : Text, original : { name : Text, package : Optional Text } }
+      List { original : { package : Optional Text, name : Text }, name : Text }
   , signatures :
       List Text
   }
diff --git a/dhall/types/License.dhall b/dhall/types/License.dhall
--- a/dhall/types/License.dhall
+++ b/dhall/types/License.dhall
@@ -1,24 +1,25 @@
-< GPL :
-    Optional ./Version.dhall
-| AGPL :
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+< AGPL :
     Optional ./Version.dhall
-| LGPL :
+| AllRightsReserved
+| Apache :
     Optional ./Version.dhall
 | BSD2
 | BSD3
 | BSD4
-| MIT
+| GPL :
+    Optional ./Version.dhall
 | ISC
+| LGPL :
+    Optional ./Version.dhall
+| MIT
 | MPL :
     ./Version.dhall
-| Apache :
-    Optional ./Version.dhall
-| PublicDomain
-| AllRightsReserved
-| Unspecified
-| Unknown :
-    Text
 | Other
+| PublicDomain
 | SPDX :
     ./SPDX.dhall
+| Unknown :
+    Text
+| Unspecified
 >
diff --git a/dhall/types/Mixin.dhall b/dhall/types/Mixin.dhall
--- a/dhall/types/Mixin.dhall
+++ b/dhall/types/Mixin.dhall
@@ -1,3 +1,4 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 { package :
     Text
 , renaming :
diff --git a/dhall/types/ModuleRenaming.dhall b/dhall/types/ModuleRenaming.dhall
--- a/dhall/types/ModuleRenaming.dhall
+++ b/dhall/types/ModuleRenaming.dhall
@@ -1,1 +1,2 @@
-< renaming : List { rename : Text, to : Text } | default | hiding : List Text >
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+< default | hiding : List Text | renaming : List { rename : Text, to : Text } >
diff --git a/dhall/types/OS.dhall b/dhall/types/OS.dhall
--- a/dhall/types/OS.dhall
+++ b/dhall/types/OS.dhall
@@ -1,3 +1,4 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < AIX
 | Android
 | DragonFly
diff --git a/dhall/types/Package.dhall b/dhall/types/Package.dhall
--- a/dhall/types/Package.dhall
+++ b/dhall/types/Package.dhall
@@ -1,7 +1,8 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 { author :
     Text
 , benchmarks :
-    List { benchmark : ./Guarded.dhall ./Benchmark.dhall, name : Text }
+    List { name : Text, benchmark : ./Config.dhall → ./Benchmark.dhall }
 , bug-reports :
     Text
 , build-type :
@@ -13,7 +14,7 @@
 , copyright :
     Text
 , custom-setup :
-    Optional ./CustomSetup.dhall
+    Optional ./SetupBuildInfo.dhall
 , data-dir :
     Text
 , data-files :
@@ -21,7 +22,7 @@
 , description :
     Text
 , executables :
-    List { executable : ./Guarded.dhall ./Executable.dhall, name : Text }
+    List { name : Text, executable : ./Config.dhall → ./Executable.dhall }
 , extra-doc-files :
     List Text
 , extra-source-files :
@@ -29,13 +30,13 @@
 , extra-tmp-files :
     List Text
 , flags :
-    List { default : Bool, description : Text, manual : Bool, name : Text }
+    List ./Flag.dhall
 , foreign-libraries :
-    List { foreign-lib : ./Guarded.dhall ./ForeignLibrary.dhall, name : Text }
+    List { name : Text, foreign-lib : ./Config.dhall → ./ForeignLibrary.dhall }
 , homepage :
     Text
 , library :
-    Optional (./Guarded.dhall ./Library.dhall)
+    Optional (./Config.dhall → ./Library.dhall)
 , license :
     ./License.dhall
 , license-files :
@@ -51,11 +52,11 @@
 , stability :
     Text
 , sub-libraries :
-    List { library : ./Guarded.dhall ./Library.dhall, name : Text }
+    List { name : Text, library : ./Config.dhall → ./Library.dhall }
 , synopsis :
     Text
 , test-suites :
-    List { name : Text, test-suite : ./Guarded.dhall ./TestSuite.dhall }
+    List { name : Text, test-suite : ./Config.dhall → ./TestSuite.dhall }
 , tested-with :
     List { compiler : ./Compiler.dhall, version : ./VersionRange.dhall }
 , version :
diff --git a/dhall/types/RepoKind.dhall b/dhall/types/RepoKind.dhall
--- a/dhall/types/RepoKind.dhall
+++ b/dhall/types/RepoKind.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < RepoHead | RepoKindUnknown : { _1 : Text } | RepoThis >
diff --git a/dhall/types/RepoType.dhall b/dhall/types/RepoType.dhall
--- a/dhall/types/RepoType.dhall
+++ b/dhall/types/RepoType.dhall
@@ -1,3 +1,4 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 < Bazaar
 | CVS
 | Darcs
diff --git a/dhall/types/SPDX.dhall b/dhall/types/SPDX.dhall
--- a/dhall/types/SPDX.dhall
+++ b/dhall/types/SPDX.dhall
@@ -1,12 +1,26 @@
-let LicenseExceptionId : Type = ./SPDX/LicenseExceptionId.dhall
-
-let LicenseId : Type = ./SPDX/LicenseId.dhall
-
-in    ∀(SPDX : Type)
-    → ∀(license : LicenseId → Optional LicenseExceptionId → SPDX)
-    → ∀(licenseVersionOrLater : LicenseId → Optional LicenseExceptionId → SPDX)
-    → ∀(ref : Text → Optional LicenseExceptionId → SPDX)
-    → ∀(refWithFile : Text → Text → Optional LicenseExceptionId → SPDX)
-    → ∀(and : SPDX → SPDX → SPDX)
-    → ∀(or : SPDX → SPDX → SPDX)
-    → SPDX
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+  ∀(SPDX : Type)
+→ ∀ ( license
+    :   ∀(id : ./SPDX/LicenseId.dhall)
+      → ∀(exception : Optional ./SPDX/LicenseExceptionId.dhall)
+      → SPDX
+    )
+→ ∀ ( licenseVersionOrLater
+    :   ∀(id : ./SPDX/LicenseId.dhall)
+      → ∀(exception : Optional ./SPDX/LicenseExceptionId.dhall)
+      → SPDX
+    )
+→ ∀ ( ref
+    :   ∀(ref : Text)
+      → ∀(exception : Optional ./SPDX/LicenseExceptionId.dhall)
+      → SPDX
+    )
+→ ∀ ( refWithFile
+    :   ∀(ref : Text)
+      → ∀(file : Text)
+      → ∀(exception : Optional ./SPDX/LicenseExceptionId.dhall)
+      → SPDX
+    )
+→ ∀(and : SPDX → SPDX → SPDX)
+→ ∀(or : SPDX → SPDX → SPDX)
+→ SPDX
diff --git a/dhall/types/SPDX/LicenseExceptionId.dhall b/dhall/types/SPDX/LicenseExceptionId.dhall
--- a/dhall/types/SPDX/LicenseExceptionId.dhall
+++ b/dhall/types/SPDX/LicenseExceptionId.dhall
@@ -1,24 +1,25 @@
-< DS389_exception
-| Autoconf_exception_2_0
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+< Autoconf_exception_2_0
 | Autoconf_exception_3_0
 | Bison_exception_2_2
 | Bootloader_exception
-| Classpath_exception_2_0
 | CLISP_exception_2_0
+| Classpath_exception_2_0
+| DS389_exception
 | DigiRule_FOSS_exception
 | ECos_exception_2_0
-| Fawkes_Runtime_exception
 | FLTK_exception
+| Fawkes_Runtime_exception
 | Font_exception_2_0
 | Freertos_exception_2_0
 | GCC_exception_2_0
 | GCC_exception_3_1
 | Gnu_javamail_exception
 | I2p_gpl_java_exception
-| Libtool_exception
-| Linux_syscall_note
 | LLVM_exception
 | LZMA_exception
+| Libtool_exception
+| Linux_syscall_note
 | Mif_exception
 | Nokia_Qt_exception_1_1
 | OCCT_exception_1_0
diff --git a/dhall/types/SPDX/LicenseId.dhall b/dhall/types/SPDX/LicenseId.dhall
--- a/dhall/types/SPDX/LicenseId.dhall
+++ b/dhall/types/SPDX/LicenseId.dhall
@@ -1,65 +1,65 @@
-< NullBSD
-| AAL
-| Abstyles
-| Adobe_2006
-| Adobe_Glyph
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+< AAL
 | ADSL
 | AFL_1_1
 | AFL_1_2
 | AFL_2_0
 | AFL_2_1
 | AFL_3_0
-| Afmparse
 | AGPL_1_0
 | AGPL_1_0_only
 | AGPL_1_0_or_later
 | AGPL_3_0_only
 | AGPL_3_0_or_later
-| Aladdin
 | AMDPLPA
 | AML
 | AMPAS
 | ANTLR_PD
-| Apache_1_0
-| Apache_1_1
-| Apache_2_0
 | APAFML
 | APL_1_0
 | APSL_1_0
 | APSL_1_1
 | APSL_1_2
 | APSL_2_0
-| Artistic_1_0_cl8
-| Artistic_1_0_Perl
+| Abstyles
+| Adobe_2006
+| Adobe_Glyph
+| Afmparse
+| Aladdin
+| Apache_1_0
+| Apache_1_1
+| Apache_2_0
 | Artistic_1_0
+| Artistic_1_0_Perl
+| Artistic_1_0_cl8
 | Artistic_2_0
-| Bahyph
-| Barr
-| Beerware
-| BitTorrent_1_0
-| BitTorrent_1_1
-| Borceux
 | BSD_1_Clause
+| BSD_2_Clause
 | BSD_2_Clause_FreeBSD
 | BSD_2_Clause_NetBSD
 | BSD_2_Clause_Patent
-| BSD_2_Clause
+| BSD_3_Clause
 | BSD_3_Clause_Attribution
 | BSD_3_Clause_Clear
 | BSD_3_Clause_LBNL
-| BSD_3_Clause_No_Nuclear_License_2014
 | BSD_3_Clause_No_Nuclear_License
+| BSD_3_Clause_No_Nuclear_License_2014
 | BSD_3_Clause_No_Nuclear_Warranty
-| BSD_3_Clause
-| BSD_4_Clause_UC
 | BSD_4_Clause
+| BSD_4_Clause_UC
 | BSD_Protection
 | BSD_Source_Code
 | BSL_1_0
+| Bahyph
+| Barr
+| Beerware
+| BitTorrent_1_0
+| BitTorrent_1_1
+| Borceux
 | Bzip2_1_0_5
 | Bzip2_1_0_6
-| Caldera
 | CATOSL_1_1
+| CC0_1_0
 | CC_BY_1_0
 | CC_BY_2_0
 | CC_BY_2_5
@@ -90,7 +90,6 @@
 | CC_BY_SA_2_5
 | CC_BY_SA_3_0
 | CC_BY_SA_4_0
-| CC0_1_0
 | CDDL_1_0
 | CDDL_1_1
 | CDLA_Permissive_1_0
@@ -101,57 +100,54 @@
 | CECILL_2_1
 | CECILL_B
 | CECILL_C
-| ClArtistic
 | CNRI_Jython
-| CNRI_Python_GPL_Compatible
 | CNRI_Python
-| Condor_1_1
+| CNRI_Python_GPL_Compatible
 | CPAL_1_0
 | CPL_1_0
 | CPOL_1_02
+| CUA_OPL_1_0
+| Caldera
+| ClArtistic
+| Condor_1_1
 | Crossword
 | CrystalStacker
-| CUA_OPL_1_0
 | Cube
 | Curl
+| DOC
+| DSDP
 | D_FSL_1_0
 | Diffmark
-| DOC
 | Dotseqn
-| DSDP
 | Dvipdfm
 | ECL_1_0
 | ECL_2_0
 | EFL_1_0
 | EFL_2_0
 | EGenix
-| Entessa
 | EPL_1_0
 | EPL_2_0
-| ErlPL_1_1
 | EUDatagrid
 | EUPL_1_0
 | EUPL_1_1
 | EUPL_1_2
+| Entessa
+| ErlPL_1_1
 | Eurosym
-| Fair
-| Frameworx_1_0
-| FreeImage
 | FSFAP
 | FSFUL
 | FSFULLR
 | FTL
+| Fair
+| Frameworx_1_0
+| FreeImage
 | GFDL_1_1_only
 | GFDL_1_1_or_later
 | GFDL_1_2_only
 | GFDL_1_2_or_later
 | GFDL_1_3_only
 | GFDL_1_3_or_later
-| Giftware
 | GL2PS
-| Glide
-| Glulxe
-| Gnuplot
 | GPL_1_0_only
 | GPL_1_0_or_later
 | GPL_2_0_only
@@ -159,40 +155,36 @@
 | GPL_3_0_only
 | GPL_3_0_or_later
 | GSOAP_1_3b
-| HaskellReport
+| Giftware
+| Glide
+| Glulxe
+| Gnuplot
 | HPND
+| HaskellReport
 | IBM_pibs
 | ICU
 | IJG
-| ImageMagick
 | IMatix
+| IPA
+| IPL_1_0
+| ISC
+| ImageMagick
 | Imlib2
 | Info_ZIP
-| Intel_ACPI
 | Intel
+| Intel_ACPI
 | Interbase_1_0
-| IPA
-| IPL_1_0
-| ISC
-| JasPer_2_0
 | JSON
+| JasPer_2_0
 | LAL_1_2
 | LAL_1_3
-| Latex2e
-| Leptonica
+| LGPLLR
 | LGPL_2_0_only
 | LGPL_2_0_or_later
 | LGPL_2_1_only
 | LGPL_2_1_or_later
 | LGPL_3_0_only
 | LGPL_3_0_or_later
-| LGPLLR
-| Libpng
-| Libtiff
-| LiLiQ_P_1_1
-| LiLiQ_R_1_1
-| LiLiQ_Rplus_1_1
-| Linux_OpenIB
 | LPL_1_0
 | LPL_1_02
 | LPPL_1_0
@@ -200,48 +192,57 @@
 | LPPL_1_2
 | LPPL_1_3a
 | LPPL_1_3c
-| MakeIndex
-| MirOS
+| Latex2e
+| Leptonica
+| LiLiQ_P_1_1
+| LiLiQ_R_1_1
+| LiLiQ_Rplus_1_1
+| Libpng
+| Libtiff
+| Linux_OpenIB
+| MIT
+| MITNFA
 | MIT_0
-| MIT_advertising
 | MIT_CMU
+| MIT_advertising
 | MIT_enna
 | MIT_feh
-| MIT
-| MITNFA
-| Motosoto
-| Mpich2
 | MPL_1_0
 | MPL_1_1
-| MPL_2_0_no_copyleft_exception
 | MPL_2_0
+| MPL_2_0_no_copyleft_exception
 | MS_PL
 | MS_RL
 | MTLL
+| MakeIndex
+| MirOS
+| Motosoto
+| Mpich2
 | Multics
 | Mup
 | NASA_1_3
-| Naumen
 | NBPL_1_0
 | NCSA
-| Net_SNMP
-| NetCDF
-| Newsletr
 | NGPL
 | NLOD_1_0
 | NLPL
-| Nokia
 | NOSL
-| Noweb
 | NPL_1_0
 | NPL_1_1
 | NPOSL_3_0
 | NRL
 | NTP
+| Naumen
+| NetCDF
+| Net_SNMP
+| Newsletr
+| Nokia
+| Noweb
+| NullBSD
 | OCCT_PL
 | OCLC_2_0
-| ODbL_1_0
 | ODC_By_1_0
+| ODbL_1_0
 | OFL_1_0
 | OFL_1_1
 | OGTSL
@@ -249,12 +250,12 @@
 | OLDAP_1_2
 | OLDAP_1_3
 | OLDAP_1_4
-| OLDAP_2_0_1
 | OLDAP_2_0
+| OLDAP_2_0_1
 | OLDAP_2_1
+| OLDAP_2_2
 | OLDAP_2_2_1
 | OLDAP_2_2_2
-| OLDAP_2_2
 | OLDAP_2_3
 | OLDAP_2_4
 | OLDAP_2_5
@@ -262,7 +263,6 @@
 | OLDAP_2_7
 | OLDAP_2_8
 | OML
-| OpenSSL
 | OPL_1_0
 | OSET_PL_2_1
 | OSL_1_0
@@ -270,6 +270,7 @@
 | OSL_2_0
 | OSL_2_1
 | OSL_3_0
+| OpenSSL
 | PDDL_1_0
 | PHP_3_0
 | PHP_3_01
@@ -278,36 +279,36 @@
 | Psfrag
 | Psutils
 | Python_2_0
-| Qhull
 | QPL_1_0
-| Rdisc
+| Qhull
 | RHeCos_1_1
 | RPL_1_1
 | RPL_1_5
 | RPSL_1_0
 | RSA_MD
 | RSCPL
+| Rdisc
 | Ruby
 | SAX_PD
-| Saxpath
 | SCEA
-| Sendmail
 | SGI_B_1_0
 | SGI_B_1_1
 | SGI_B_2_0
-| SimPL_2_0
-| SISSL_1_2
 | SISSL
-| Sleepycat
+| SISSL_1_2
 | SMLNJ
 | SMPPL
 | SNIA
+| SPL_1_0
+| SWL
+| Saxpath
+| Sendmail
+| SimPL_2_0
+| Sleepycat
 | Spencer_86
 | Spencer_94
 | Spencer_99
-| SPL_1_0
 | SugarCRM_1_1_3
-| SWL
 | TCL
 | TCP_wrappers
 | TMate
@@ -315,36 +316,36 @@
 | TOSL
 | TU_Berlin_1_0
 | TU_Berlin_2_0
+| UPL_1_0
 | Unicode_DFS_2015
 | Unicode_DFS_2016
 | Unicode_TOU
 | Unlicense
-| UPL_1_0
-| Vim
 | VOSTROM
 | VSL_1_0
+| Vim
+| W3C
 | W3C_19980720
 | W3C_20150513
-| W3C
+| WTFPL
 | Watcom_1_0
 | Wsuipa
-| WTFPL
 | X11
-| Xerox
 | XFree86_1_1
+| XSkat
+| Xerox
 | Xinetd
 | Xnet
 | Xpp
-| XSkat
 | YPL_1_0
 | YPL_1_1
+| ZPL_1_1
+| ZPL_2_0
+| ZPL_2_1
 | Zed
 | Zend_2_0
 | Zimbra_1_3
 | Zimbra_1_4
-| Zlib_acknowledgement
 | Zlib
-| ZPL_1_1
-| ZPL_2_0
-| ZPL_2_1
+| Zlib_acknowledgement
 >
diff --git a/dhall/types/Scope.dhall b/dhall/types/Scope.dhall
--- a/dhall/types/Scope.dhall
+++ b/dhall/types/Scope.dhall
@@ -1,1 +1,2 @@
-< Public | Private >
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+< Private | Public >
diff --git a/dhall/types/SetupBuildInfo.dhall b/dhall/types/SetupBuildInfo.dhall
--- a/dhall/types/SetupBuildInfo.dhall
+++ b/dhall/types/SetupBuildInfo.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 { setup-depends : List ./Dependency.dhall }
diff --git a/dhall/types/SourceRepo.dhall b/dhall/types/SourceRepo.dhall
--- a/dhall/types/SourceRepo.dhall
+++ b/dhall/types/SourceRepo.dhall
@@ -1,15 +1,16 @@
-{ type :
-    Optional ./RepoType.dhall
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+{ branch :
+    Optional Text
+, kind :
+    ./RepoKind.dhall
 , location :
     Optional Text
 , module :
     Optional Text
-, branch :
+, subdir :
     Optional Text
 , tag :
     Optional Text
-, subdir :
-    Optional Text
-, kind :
-    ./RepoKind.dhall
+, type :
+    Optional ./RepoType.dhall
 }
diff --git a/dhall/types/TestSuite.dhall b/dhall/types/TestSuite.dhall
--- a/dhall/types/TestSuite.dhall
+++ b/dhall/types/TestSuite.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 ./BuildInfo.dhall ⩓ { type : ./TestType.dhall }
diff --git a/dhall/types/TestType.dhall b/dhall/types/TestType.dhall
--- a/dhall/types/TestType.dhall
+++ b/dhall/types/TestType.dhall
@@ -1,1 +1,2 @@
-< exitcode-stdio : { main-is : Text } | detailed : { module : Text } >
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
+< detailed : { module : Text } | exitcode-stdio : { main-is : Text } >
diff --git a/dhall/types/Version.dhall b/dhall/types/Version.dhall
--- a/dhall/types/Version.dhall
+++ b/dhall/types/Version.dhall
@@ -1,1 +1,2 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
 ∀(Version : Type) → ∀(v : Text → Version) → Version
diff --git a/dhall/types/VersionRange.dhall b/dhall/types/VersionRange.dhall
--- a/dhall/types/VersionRange.dhall
+++ b/dhall/types/VersionRange.dhall
@@ -1,3 +1,4 @@
+-- This file is auto-generated by dhall-to-cabal-meta. Look but don't touch (unless you want your edits to be over-written).
   ∀(VersionRange : Type)
 → ∀(anyVersion : VersionRange)
 → ∀(noVersion : VersionRange)
diff --git a/exe/Main.hs b/exe/Main.hs
--- a/exe/Main.hs
+++ b/exe/Main.hs
@@ -7,18 +7,12 @@
 
 module Main ( main ) where
 
-import Control.Applicative ( (<**>), Const(..), optional, (<|>) )
-import Control.Monad ( guard )
+import Control.Applicative ( (<**>), optional, (<|>) )
+import Control.Monad ( join )
 import Data.Char ( isAlphaNum )
-import Control.Monad.Trans.Class ( lift )
-import Control.Monad.Trans.State ( State, execState, get, modify, put )
-import Control.Monad.Trans.Writer ( WriterT, execWriterT, tell )
-import Data.Foldable ( asum, traverse_ )
+import Data.Foldable ( asum, foldl' )
 import Data.Function ( (&) )
-import Data.Functor.Product ( Product(..) )
-import Data.Functor.Identity ( Identity(..) )
 import Data.List.NonEmpty ( NonEmpty(..) )
-import Data.Monoid ( Any(..) )
 import Data.Maybe ( fromMaybe )
 import Data.Text (Text)
 import Data.String ( fromString )
@@ -30,20 +24,19 @@
 import CabalToDhall ( KnownDefault, getDefault, resolvePreludeVar )
 import DhallLocation ( preludeLocation, typesLocation, dhallFromGitHub )
 import DhallToCabal
+import DhallToCabal.FactorType ( KnownType(..), factored, mapWithBindings )
 import DhallToCabal.Util ( relativeTo )
 import qualified Paths_dhall_to_cabal as Paths
 
-import qualified Data.List.NonEmpty as NonEmpty
 import qualified Data.Text.IO as StrictText
 import qualified Data.Text.Prettyprint.Doc as Pretty
 import qualified Data.Text.Prettyprint.Doc.Render.Text as Pretty
 import qualified Dhall
 import qualified Dhall.Core as Dhall
-import qualified Dhall.Core as Expr ( Expr(..), Var(..), Binding(..), shift )
-import qualified Distribution.PackageDescription as Cabal
-import qualified Dhall.Map as Map
+import qualified Dhall.Core as Expr ( Expr(..), Binding(..) )
+import qualified Dhall.Lint as Lint
 import qualified Dhall.Parser
-import qualified Dhall.TypeCheck as Dhall
+import qualified Distribution.PackageDescription as Cabal
 import qualified Distribution.PackageDescription.PrettyPrint as Cabal
 import qualified Distribution.Types.PackageId as Cabal
 import qualified Distribution.Types.PackageName as Cabal
@@ -58,46 +51,6 @@
   | PrintVersion
 
 
-
-data KnownType
-  = Library
-  | ForeignLibrary
-  | Benchmark
-  | Executable
-  | TestSuite
-  | BuildInfo
-  | Config
-  | SourceRepo
-  | RepoType
-  | RepoKind
-  | Compiler
-  | OS
-  | Extension
-  | CompilerOptions
-  | Arch
-  | Language
-  | License
-  | BuildType
-  | Package
-  | VersionRange
-  | Version
-  | SPDX
-  | LicenseId
-  | LicenseExceptionId
-  | Scope
-  | ModuleRenaming
-  deriving (Bounded, Enum, Eq, Ord, Read, Show)
-
-
--- | A 'Benchmark' is a proper subrecord of an 'Executable', but we
--- don't want to write 'Executable' in terms of 'Benchmark'! Hence,
--- limit which types we will do the common-field extraction for to
--- only 'BuildInfo', for the time being.
-isCandidateSubrecord :: KnownType -> Bool
-isCandidateSubrecord BuildInfo = True
-isCandidateSubrecord _ = False
-
-
 shouldBeImported :: KnownType -> Bool
 shouldBeImported Extension = True
 shouldBeImported LicenseId = True
@@ -105,7 +58,6 @@
 shouldBeImported _ = False
 
 
-
 data DhallToCabalOptions = DhallToCabalOptions
   { dhallFilePath :: Maybe String
   , explain :: Bool
@@ -394,36 +346,12 @@
     { Pretty.layoutPageWidth = Pretty.AvailablePerLine 80 1.0 }
 
 
-
-data CSEState a = CSEState
-  { _factoredOutTypes :: [ ( KnownType, Expr.Expr Dhall.Parser.Src a ) ]
-    -- ^ Things we've already factored out (which may undergo further factoring themselves).
-  , _rootType :: Expr.Expr Dhall.Parser.Src a
-    -- ^ The original type, which we started factoring things from.
-  }
-
-
-traverseTypes
-  :: ( Applicative f )
-  => ( Expr.Expr Dhall.Parser.Src a -> f ( Expr.Expr Dhall.Parser.Src b ) )
-  -> CSEState a
-  -> f ( CSEState b )
-traverseTypes f (CSEState types root) =
-  CSEState
-    <$> traverse (traverse f) types
-    <*> f root
-
-
+-- Note: the expression is linted afterwards, so this can do the
+-- drop-dead simple thing of adding a new `let` each time.
 addBinding :: Text -> Dhall.Expr s a -> Dhall.Expr s a -> Dhall.Expr s a
-addBinding name val = \case
-  Expr.Let bindings body ->
-    Expr.Let
-      ( Expr.Binding name Nothing val `NonEmpty.cons` bindings )
-      body
-  expr ->
-    Expr.Let
-      ( Expr.Binding name Nothing val :| [] )
-      expr
+addBinding name val =
+  Expr.Let
+    ( Expr.Binding name Nothing val :| [] )
 
 
 printType :: PrintTypeOptions -> IO ()
@@ -431,387 +359,42 @@
   Pretty.renderIO
     System.IO.stdout
     ( Pretty.layoutSmart opts
-        ( Pretty.pretty factoredType )
+        ( Pretty.pretty ( Lint.lint result ) )
     )
 
   putStrLn ""
 
   where
 
-    dhallType :: KnownType -> Dhall.Expr Dhall.Parser.Src a
-    dhallType t = fmap Dhall.absurd
-      ( case t of
-          Config -> configRecordType
-          Library -> Dhall.expected library
-          ForeignLibrary -> Dhall.expected foreignLib
-          Executable -> Dhall.expected executable
-          Benchmark -> Dhall.expected benchmark
-          TestSuite -> Dhall.expected testSuite
-          BuildInfo -> buildInfoType
-          SourceRepo -> Dhall.expected sourceRepo
-          RepoType -> Dhall.expected repoType
-          RepoKind -> Dhall.expected repoKind
-          Compiler -> Dhall.expected compilerFlavor
-          OS -> Dhall.expected operatingSystem
-          Extension -> Dhall.expected extension
-          CompilerOptions -> Dhall.expected compilerOptions
-          Arch -> Dhall.expected arch
-          Language -> Dhall.expected language
-          License -> Dhall.expected license
-          BuildType -> Dhall.expected buildType
-          Package -> Dhall.expected genericPackageDescription
-          VersionRange -> Dhall.expected versionRange
-          Version -> Dhall.expected version
-          SPDX -> Dhall.expected spdxLicense
-          LicenseId -> Dhall.expected spdxLicenseId
-          LicenseExceptionId -> Dhall.expected spdxLicenseExceptionId
-          Scope -> Dhall.expected executableScope
-          ModuleRenaming -> Dhall.expected moduleRenaming
-      )
+    linkedType =
+      join . mapWithBindings linkType . factored
 
-    makeLetOrImport t val reduced =
+    linkType var t =
       let
         name = fromString ( show t )
       in if shouldBeImported t && not selfContained
-         then Dhall.subst ( Expr.V name 0 ) ( Expr.Var ( Expr.V "types" 0 ) `Expr.Field` name ) reduced
-         else addBinding name val reduced
-
-    factoredType :: Expr.Expr Dhall.Parser.Src Dhall.Import
-    factoredType =
-      let
-        initialState :: CSEState Dhall.Import
-        initialState = CSEState mempty ( dhallType typeToPrint )
-
-        CSEState types body =
-          execState
-            ( traverse_ step [ minBound .. maxBound ] )
-            initialState
-
-        importing = if any shouldBeImported ( fst <$> types ) && not selfContained
-          then addBinding
-                 "types"
-                 ( Expr.Embed ( typesLocation dhallFromGitHub ) )
-          else id
-
-      in
-        importing body
-
-    step
-      :: (Eq a)
-      => KnownType
-         -- ^ Name of the type we're trying to factor out
-      -> State ( CSEState a ) ()
-    step factorType = do
-      Any usedFactor <- execWriterT
-        ( lift . put =<< traverseTypes ( tryCSE factorType ) =<< lift get )
-      let
-        addingUsedFactor =
-          if usedFactor then ( ( factorType, ( dhallType factorType ) ) : ) else id
-      modify $ \ ( CSEState types expr ) -> CSEState ( addingUsedFactor types ) expr
-
-    tryCSE
-      :: (Eq a, Monad m)
-      => KnownType -- ^ The type we're factoring out
-      -> Expr.Expr Dhall.Parser.Src a
-      -> WriterT Any m ( Expr.Expr Dhall.Parser.Src a )
-    tryCSE factorType expr =
-      let
-        name = fromString ( show factorType )
-        subrecord = isCandidateSubrecord factorType
-      in
-        case liftCSE subrecord name ( dhallType factorType ) expr of
-          Just expr' -> do
-            tell (Any True)
-            -- Note that the substitution/binding only happens in this
-            -- branch, rather than the branch where no expression was
-            -- lifted out - otherwise, as the variables aren't shifted
-            -- in the latter case, we can get into trouble.
-            return $ makeLetOrImport factorType ( dhallType factorType ) expr'
-          Nothing ->
-            return expr
-
-
-liftCSE
-  :: (Eq s, Eq a)
-  => Bool
-     -- ^ Should we attempt to find the subexpression as a sub-record?
-  -> Text
-     -- ^ The name of the binding
-  -> Expr.Expr s a
-     -- ^ The common subexpression to lift
-  -> Expr.Expr s a
-     -- ^ The expression to remove a common subexpression from
-  -> Maybe (Expr.Expr s a)
-  -- ^ 'Just' the CSE-ed expression, or Nothing if the subexpression wasn't found.
-liftCSE subrecord name body expr =
-  let
-    v0 =
-      Expr.V name 0
-
-  in
-    case go ( Expr.shift 1 v0 expr ) v0 of
-      Pair ( Const ( Any False ) ) _ ->
-        -- There was nothing to lift
-        Nothing
-
-      Pair _ ( Identity reduced ) | reduced == Expr.Var v0 ->
-        -- We lifted the whole expression out. This is not a win, so don't bother.
-        Nothing
-
-      Pair _ ( Identity reduced ) ->
-        Just reduced
-
-  where
-
-    shiftName n v | n == name =
-      shiftVar 1 v
-
-    shiftName _ v =
-        v
-
-    shiftVar delta ( Expr.V name' n ) =
-      Expr.V name' ( n + delta )
-
-    subtractRecordFields a b = do
-      guard subrecord
-
-      Expr.Record left <-
-        return a
-
-      Expr.Record right <-
-        return b
-
-      let
-        intersection =
-          Map.intersectionWith (==) left right
-
-      -- The right record cannot have any fields not in left.
-      guard ( null ( Map.difference right left ) )
-
-      -- We must have at least one field with a common name
-      guard ( not ( null intersection ) )
-
-      -- All common fields must have identical types
-      guard ( and intersection )
-
-      let
-        extra =
-          Map.difference left right
-
-      guard ( not ( null extra ) )
-
-      return ( Expr.Record extra )
-
-    go e v | e == body =
-      Pair ( Const ( Any True ) ) ( Identity ( Expr.Var v ) )
-
-    go ( ( `subtractRecordFields` body ) -> Just extra ) v =
-      Pair
-        ( Const ( Any True ) )
-        ( Identity ( Expr.CombineTypes ( Expr.Var v ) extra ) )
-
-    go e v =
-      case e of
-        Expr.Lam n t b ->
-          Expr.Lam n t <$> go b ( shiftName n v )
-
-        Expr.Pi n t b ->
-          Expr.Pi n <$> go t v <*> go b ( shiftName n v )
-
-        Expr.App f a ->
-          Expr.App <$> go f v <*> go a v
-
-        Expr.Let bs e ->
-          Expr.Let <$> traverse go' bs <*> go e shifted
-            where go' (Expr.Binding n t b) = Expr.Binding n t <$> go b v
-                  shifted = foldr (shiftName . Expr.variable) v bs
-
-        Expr.Annot a b ->
-          Expr.Annot <$> go a v <*> go b v
-
-        Expr.BoolAnd a b ->
-          Expr.BoolAnd <$> go a v <*> go b v
-
-        Expr.BoolOr a b ->
-          Expr.BoolOr <$> go a v <*> go b v
-
-        Expr.BoolEQ a b ->
-          Expr.BoolEQ <$> go a v <*> go b v
-
-        Expr.BoolNE a b ->
-          Expr.BoolNE <$> go a v <*> go b v
-
-        Expr.BoolIf a b c ->
-          Expr.BoolIf <$> go a v <*> go b v <*> go c v
-
-        Expr.NaturalPlus a b ->
-          Expr.NaturalPlus <$> go a v <*> go b v
-
-        Expr.NaturalTimes a b ->
-          Expr.NaturalTimes <$> go a v <*> go b v
-
-        Expr.ListAppend a b ->
-          Expr.ListAppend <$> go a v <*> go b v
-
-        Expr.Combine a b ->
-          Expr.Combine <$> go a v <*> go b v
-
-        Expr.Prefer a b ->
-          Expr.Prefer <$> go a v <*> go b v
-
-        Expr.TextAppend a b ->
-          Expr.TextAppend <$> go a v <*> go b v
-
-        Expr.ListLit t elems ->
-          Expr.ListLit
-            <$> ( traverse ( `go` v ) t )
-            <*> ( traverse ( `go` v ) elems )
-
-        Expr.OptionalLit t elems ->
-          Expr.OptionalLit
-            <$> go t v
-            <*> ( traverse ( `go` v ) elems )
-
-        Expr.Some a ->
-          Expr.Some <$> go a v
-
-        Expr.None ->
-          pure Expr.None
-
-        Expr.Record fields ->
-          Expr.Record <$> traverse ( `go` v ) fields
-
-        Expr.RecordLit fields ->
-          Expr.RecordLit <$> traverse ( `go` v ) fields
-
-        Expr.Union fields ->
-          Expr.Union <$> traverse ( traverse ( `go` v ) ) fields
-
-        Expr.UnionLit n a fields ->
-          Expr.UnionLit n <$> go a v <*> traverse ( traverse ( `go` v ) ) fields
-
-        Expr.Merge a b t ->
-          Expr.Merge <$> go a v <*> go b v <*> traverse ( `go` v ) t
-
-        Expr.Field e f ->
-          Expr.Field <$> go e v <*> pure f
-
-        Expr.Note s e ->
-          Expr.Note s <$> go e v
-
-        Expr.CombineTypes a b ->
-          Expr.CombineTypes <$> go a v <*> go b v
-
-        Expr.Project e fs ->
-          Expr.Project <$> go e v <*> pure fs
-
-        Expr.ImportAlt l r ->
-          Expr.ImportAlt <$> go l v <*> go r v
-
-        Expr.IntegerToDouble ->
-          pure Expr.IntegerToDouble
-
-        Expr.Embed{} ->
-          pure e
-
-        Expr.Const{} ->
-          pure e
-
-        Expr.Var{} ->
-          pure e
-
-        Expr.Bool{} ->
-          pure e
-
-        Expr.BoolLit{} ->
-          pure e
-
-        Expr.Natural{} ->
-          pure e
-
-        Expr.NaturalLit{} ->
-          pure e
-
-        Expr.NaturalFold{} ->
-          pure e
-
-        Expr.NaturalBuild{} ->
-          pure e
-
-        Expr.NaturalIsZero{} ->
-          pure e
-
-        Expr.NaturalEven{} ->
-          pure e
-
-        Expr.NaturalOdd{} ->
-          pure e
-
-        Expr.NaturalToInteger{} ->
-          pure e
-
-        Expr.NaturalShow{} ->
-          pure e
-
-        Expr.Integer{} ->
-          pure e
-
-        Expr.IntegerShow{} ->
-          pure e
-
-        Expr.IntegerLit{} ->
-          pure e
-
-        Expr.Double{} ->
-          pure e
-
-        Expr.DoubleShow{} ->
-          pure e
-
-        Expr.DoubleLit{} ->
-          pure e
-
-        Expr.Text{} ->
-          pure e
-
-        Expr.TextLit{} ->
-          pure e
-
-        Expr.TextShow ->
-          pure e
-
-        Expr.List ->
-          pure e
-
-        Expr.ListBuild ->
-          pure e
-
-        Expr.ListFold ->
-          pure e
-
-        Expr.ListLength ->
-          pure e
-
-        Expr.ListHead ->
-          pure e
-
-        Expr.ListLast ->
-          pure e
-
-        Expr.ListIndexed ->
-          pure e
+         then Expr.Var ( var "types" ) `Expr.Field` name
+         else Expr.Var ( var name )
 
-        Expr.ListReverse ->
-          pure e
+    bindTypes expr =
+      foldl' bindType expr [ minBound .. maxBound ]
 
-        Expr.Optional ->
-          pure e
+    bindType expr t =
+      addBinding
+        ( fromString ( show t ) )
+        ( linkedType t )
+        expr
 
-        Expr.OptionalFold ->
-          pure e
+    bindImport =
+      addBinding
+        "types"
+        ( Expr.Embed ( typesLocation dhallFromGitHub ) )
 
-        Expr.OptionalBuild ->
-          pure e
+    -- Unconditionally add everything, since lint will remove the
+    -- redundant bindings.
+    result =
+      bindImport
+        ( bindTypes ( linkedType typeToPrint ) )
 
 
 printVersion :: IO ()
@@ -824,7 +407,7 @@
   Pretty.renderIO
     System.IO.stdout
     ( Pretty.layoutSmart opts
-        ( Pretty.pretty ( withPreludeImport expr ) )
+        ( Pretty.pretty ( Lint.lint ( withPreludeImport expr ) ) )
     )
 
   putStrLn ""
diff --git a/golden-tests/cabal-to-dhall/repotypeunknown.cabal b/golden-tests/cabal-to-dhall/repotypeunknown.cabal
new file mode 100644
--- /dev/null
+++ b/golden-tests/cabal-to-dhall/repotypeunknown.cabal
@@ -0,0 +1,7 @@
+cabal-version: 2.4
+name: foo
+version: 2
+
+source-repository blargh
+  type: git
+  location: https://example.com
diff --git a/golden-tests/cabal-to-dhall/repotypeunknown.dhall b/golden-tests/cabal-to-dhall/repotypeunknown.dhall
new file mode 100644
--- /dev/null
+++ b/golden-tests/cabal-to-dhall/repotypeunknown.dhall
@@ -0,0 +1,22 @@
+let prelude = ./../../dhall/prelude.dhall
+
+let types = ./../../dhall/types.dhall
+
+in    prelude.defaults.Package
+    ⫽ { name =
+          "foo"
+      , version =
+          prelude.v "2"
+      , cabal-version =
+          prelude.v "2.4"
+      , source-repos =
+          [   prelude.defaults.SourceRepo
+            ⫽ { type =
+                  Some types.RepoType.Git
+              , location =
+                  Some "https://example.com"
+              , kind =
+                  types.RepoKind.RepoKindUnknown { _1 = "blargh" }
+              }
+          ]
+      }
diff --git a/lib/CabalToDhall.hs b/lib/CabalToDhall.hs
--- a/lib/CabalToDhall.hs
+++ b/lib/CabalToDhall.hs
@@ -18,9 +18,7 @@
 import Data.Foldable ( foldMap )
 import Data.Functor.Contravariant ( (>$<), Contravariant( contramap ) )
 import Data.List.NonEmpty ( NonEmpty(..) )
-import Data.Monoid ( First(..) )
 import Data.Semigroup ( Semigroup, (<>) )
-import GHC.Stack
 import Numeric.Natural ( Natural )
 
 import qualified Data.ByteString as ByteString
@@ -77,10 +75,6 @@
 import DhallToCabal.ConfigTree ( ConfigTree(..) )
 
 
-type DhallExpr =
-  Dhall.Core.Expr Dhall.Parser.Src Dhall.TypeCheck.X
-
-
 dhallString :: String -> Expr.Expr s a
 dhallString = Expr.TextLit . Dhall.Core.Chunks [] . StrictText.pack
 
@@ -743,49 +737,7 @@
   identName e =
     StrictText.pack ( show e )
 
-newtype Union a =
-  Union
-    { _unUnion ::
-        ( a ->
-          ( First ( Dhall.Text, DhallExpr )
-          , Map.Map Dhall.Text DhallExpr
-          )
-        , Map.Map Dhall.Text DhallExpr
-        )
-    }
-  deriving ( Semigroup, Monoid )
 
-
-runUnion :: ( HasCallStack, Show a ) => Union a -> Dhall.InputType a
-runUnion ( Union ( f, t ) ) =
-  Dhall.InputType
-    { Dhall.embed =
-        \a ->
-          case f a of
-            ( First Nothing, _ ) ->
-              error $ "Union did not match anything. Given " ++ show a
-
-            ( First ( Just ( k, v ) ), alts ) ->
-              Expr.UnionLit k v ( Just <$> alts )
-    , Dhall.declared =
-        sortExpr ( Expr.Union ( Just <$> t ) )
-    }
-
-
-unionAlt :: Dhall.Text -> ( a -> Maybe b ) -> Dhall.InputType b -> Union a
-unionAlt k f t =
-  Union
-    ( \a ->
-        case f a of
-          Nothing ->
-            ( mempty, Map.singleton k ( Dhall.declared t ) )
-
-          Just _ ->
-            ( First ( fmap ( \b -> ( k, Dhall.embed t b ) ) ( f a ) ), mempty )
-    , Map.singleton k ( Dhall.declared t )
-    )
-
-
 maybeToDhall :: Dhall.InputType a -> Dhall.InputType ( Maybe a )
 maybeToDhall t =
   Dhall.InputType
@@ -952,7 +904,7 @@
           Expr.Var "types" `Expr.Field` "RepoKind" `Expr.Field` "RepoHead"
         Cabal.RepoKindUnknown str ->
           Expr.App
-            ( Expr.Var "types" `Expr.Field` "RepoKind" `Expr.Field` "RepoThis" )
+            ( Expr.Var "types" `Expr.Field` "RepoKind" `Expr.Field` "RepoKindUnknown" )
             ( Expr.RecordLit ( Map.singleton "_1" ( dhallString str ) ) )
     , Dhall.declared =
         Expr.Var "types" `Expr.Field` "RepoKind"
@@ -1029,7 +981,7 @@
       )
   )
     { Dhall.declared =
-        Expr.Var "types" `Expr.Field` "CustomSetup"
+        Expr.Var "types" `Expr.Field` "SetupBuildInfo"
     }
 
 
@@ -1237,27 +1189,52 @@
 
 arch :: Dhall.InputType Cabal.Arch
 arch =
-  runUnion
-    ( mconcat
-        [ unionAlt "I386" ( \x -> case x of Cabal.I386 -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "X86_64" ( \x -> case x of Cabal.X86_64 -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "PPC" ( \x -> case x of Cabal.PPC -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "PPC64" ( \x -> case x of Cabal.PPC64 -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Sparc" ( \x -> case x of Cabal.Sparc -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Arm" ( \x -> case x of Cabal.Arm -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Mips" ( \x -> case x of Cabal.Mips -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "SH" ( \x -> case x of Cabal.SH -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "IA64" ( \x -> case x of Cabal.IA64 -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "S390" ( \x -> case x of Cabal.S390 -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Alpha" ( \x -> case x of Cabal.Alpha -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Hppa" ( \x -> case x of Cabal.Hppa -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Rs6000" ( \x -> case x of Cabal.Rs6000 -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "M68k" ( \x -> case x of Cabal.M68k -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Vax" ( \x -> case x of Cabal.Vax -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "JavaScript" ( \x -> case x of Cabal.JavaScript -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "OtherArch" ( \x -> case x of Cabal.OtherArch s -> Just s ; _ -> Nothing ) ( runRecordInputType ( recordField "_1" stringToDhall ) )
-        ]
-    )
+  Dhall.InputType
+    { Dhall.embed = \case
+        Cabal.I386 ->
+          arch "I386"
+        Cabal.X86_64 ->
+          arch "X86_64"
+        Cabal.PPC ->
+          arch "PPC"
+        Cabal.PPC64 ->
+          arch "PPC64"
+        Cabal.Sparc ->
+          arch "Sparc"
+        Cabal.Arm ->
+          arch "Arm"
+        Cabal.Mips ->
+          arch "Mips"
+        Cabal.SH ->
+          arch "SH"
+        Cabal.IA64 ->
+          arch "IA64"
+        Cabal.S390 ->
+          arch "S390"
+        Cabal.Alpha ->
+          arch "Alpha"
+        Cabal.Hppa ->
+          arch "Hppa"
+        Cabal.Rs6000 ->
+          arch "Rs6000"
+        Cabal.M68k ->
+          arch "M68k"
+        Cabal.Vax ->
+          arch "Vax"
+        Cabal.JavaScript ->
+          arch "JavaScript"
+        Cabal.AArch64 ->
+          arch "AArch64"
+        Cabal.OtherArch s ->
+          Expr.App
+            ( arch "OtherArch" )
+            ( Expr.RecordLit ( Map.singleton "_1" ( dhallString s ) ) )
+    , Dhall.declared =
+        Expr.Var "types" `Expr.Field` "Arch"
+    }
+  where
+  arch name =
+    Expr.Var "types" `Expr.Field` "Arch" `Expr.Field` name
 
 
 buildInfoRecord :: RecordInputType Cabal.BuildInfo
@@ -1365,17 +1342,22 @@
 
 language :: Dhall.InputType Cabal.Language
 language =
-  ( runUnion
-      ( mconcat
-          [ unionAlt "Haskell2010" ( \x -> case x of Cabal.Haskell2010 -> Just () ; _ -> Nothing ) Dhall.inject
-          , unionAlt "UnknownLanguage" ( \x -> case x of Cabal.UnknownLanguage s -> Just s ; _ -> Nothing ) ( runRecordInputType ( recordField "_1" stringToDhall ) )
-          , unionAlt "Haskell98" ( \x -> case x of Cabal.Haskell98 -> Just () ; _ -> Nothing ) Dhall.inject
-          ]
-      )
-  )
-    { Dhall.declared =
+  Dhall.InputType
+    { Dhall.embed = \case
+        Cabal.Haskell2010 ->
+          lang "Haskell2010"
+        Cabal.Haskell98 ->
+          lang "Haskell98"
+        Cabal.UnknownLanguage s ->
+          Expr.App
+            ( lang "UnknownLanguage" )
+            ( Expr.RecordLit ( Map.singleton "_1" ( dhallString s ) ) )
+    , Dhall.declared =
         Expr.Var "types" `Expr.Field` "Language"
     }
+  where
+    lang name =
+      Expr.Var "types" `Expr.Field` "Language" `Expr.Field` name
 
 extension :: Dhall.InputType Cabal.Extension
 extension =
@@ -1526,32 +1508,28 @@
 
 testSuiteInterface :: Dhall.InputType Cabal.TestSuiteInterface
 testSuiteInterface =
-  runUnion
-    ( mconcat
-        [ unionAlt
-            "exitcode-stdio"
-            ( \x ->
-                case x of
-                  Cabal.TestSuiteExeV10 _ main ->
-                    Just main
-
-                  _ ->
-                    Nothing
+  Dhall.InputType
+    { Dhall.embed = \case
+        Cabal.TestSuiteExeV10 _ main ->
+          Expr.App
+            ( interface "exitcode-stdio" )
+            ( Dhall.embed
+              ( runRecordInputType ( recordField "main-is" stringToDhall ) )
+              main
             )
-            ( runRecordInputType ( recordField "main-is" stringToDhall ) )
-        , unionAlt
-            "detailed"
-            ( \x ->
-                case x of
-                  Cabal.TestSuiteLibV09 _ m ->
-                    Just m
-
-                  _ ->
-                    Nothing
+        Cabal.TestSuiteLibV09 _ m ->
+          Expr.App
+            ( interface "detailed" )
+            ( Dhall.embed
+              ( runRecordInputType ( recordField "module" moduleName ) )
+              m
             )
-            ( runRecordInputType ( recordField "module" moduleName ) )
-        ]
-    )
+    , Dhall.declared =
+        Expr.Var "types" `Expr.Field` "TestType"
+    }
+  where
+  interface name =
+    Expr.Var "types" `Expr.Field` "TestType" `Expr.Field` name
 
 
 executable :: Dhall.InputType Cabal.Executable
@@ -1614,16 +1592,26 @@
 
 foreignLibOption :: Dhall.InputType Cabal.ForeignLibOption
 foreignLibOption =
-  runUnion
-    ( unionAlt "Standalone" ( \x -> case x of Cabal.ForeignLibStandalone -> Just () ) Dhall.inject
-    )
+  Dhall.InputType
+    { Dhall.embed = \case
+        Cabal.ForeignLibStandalone ->
+          Expr.Var "types" `Expr.Field` "ForeignLibOption" `Expr.Field` "Standalone"
+    , Dhall.declared =
+        Expr.Var "types" `Expr.Field` "ForeignLibOption"
+    }
 
 
 foreignLibType :: Dhall.InputType Cabal.ForeignLibType
 foreignLibType =
-  runUnion
-    ( mconcat
-        [ unionAlt "Shared" ( \x -> case x of Cabal.ForeignLibNativeShared -> Just () ; _ -> Nothing ) Dhall.inject
-        , unionAlt "Static" ( \x -> case x of Cabal.ForeignLibNativeStatic -> Just () ; _ -> Nothing ) Dhall.inject
-        ]
-    )
+  Dhall.InputType
+    { Dhall.embed = \case
+        Cabal.ForeignLibNativeShared ->
+          ty "Shared"
+        Cabal.ForeignLibNativeStatic ->
+          ty "Static"
+    , Dhall.declared =
+        Expr.Var "types" `Expr.Field` "ForeignLibType"
+    }
+  where
+  ty name =
+    Expr.Var "types" `Expr.Field` "ForeignLibType" `Expr.Field` name
diff --git a/lib/DhallToCabal.hs b/lib/DhallToCabal.hs
--- a/lib/DhallToCabal.hs
+++ b/lib/DhallToCabal.hs
@@ -36,6 +36,13 @@
   , buildInfoType
   , executableScope
   , moduleRenaming
+  , foreignLibOption
+  , foreignLibType
+  , setupBuildInfo
+  , dependency
+  , testSuiteInterface
+  , mixin
+  , flag
 
   , sortExpr
   ) where
diff --git a/lib/DhallToCabal/ConfigTree.hs b/lib/DhallToCabal/ConfigTree.hs
--- a/lib/DhallToCabal/ConfigTree.hs
+++ b/lib/DhallToCabal/ConfigTree.hs
@@ -7,6 +7,7 @@
 import Control.Monad
 import Data.Semigroup ( Semigroup ( (<>) ) )
 import Dhall.Core hiding ( Const )
+import Dhall.Optics ( transformMOf )
 
 
 -- | 'ConfigTree' captures a logic-monad like expansion of the result of
@@ -87,13 +88,3 @@
     isConfigUse (App (App (Field (Var x') "impl") _) _) | v == x' = True
     isConfigUse (App (Field (Var x') "flag") _)         | v == x' = True
     isConfigUse _ = False
-
-
--- | Transform every element in a tree using a user supplied 'Traversal' in a
--- bottom-up manner with a monadic effect.
-transformMOf
-  :: Monad m =>
-  ( ( t -> m b ) -> t -> m a ) -> ( a -> m b ) -> t -> m b
-transformMOf l f = go where
-  go t = l go t >>= f
-{-# INLINE transformMOf #-}
diff --git a/lib/DhallToCabal/FactorType.hs b/lib/DhallToCabal/FactorType.hs
new file mode 100644
--- /dev/null
+++ b/lib/DhallToCabal/FactorType.hs
@@ -0,0 +1,444 @@
+{-# language LambdaCase #-}
+{-# language NoMonomorphismRestriction #-}
+{-# language NamedFieldPuns #-}
+{-# language OverloadedStrings #-}
+{-# language RecordWildCards #-}
+{-# language ViewPatterns #-}
+
+module DhallToCabal.FactorType
+  ( KnownType(..)
+  , factored
+  , mapWithBindings
+  )
+  where
+
+import Control.Monad ( guard )
+import Data.Foldable ( foldl', toList )
+import Data.Maybe ( fromMaybe )
+import Data.Text (Text)
+import Dhall.Optics ( transformOf )
+import Lens.Micro ( over )
+
+import DhallToCabal
+import Dhall.Extra
+  ( sortExpr )
+
+import qualified Data.Map as UnorderedMap
+import qualified Dhall
+import qualified Dhall.Core as Dhall
+import qualified Dhall.Core as Expr ( Expr(..), Var(..), Binding(..) )
+import qualified Dhall.Map as Map
+import qualified Dhall.Parser
+import qualified Dhall.TypeCheck as Dhall
+
+
+-- Note: this needs to be in topological order of CSEability, from
+-- big to small.
+data KnownType
+  = Package
+  | Library
+  | ForeignLibrary
+  | Benchmark
+  | Executable
+  | TestSuite
+  | SetupBuildInfo
+  | BuildInfo
+  | Config
+  | SourceRepo
+  | RepoType
+  | RepoKind
+  | Compiler
+  | OS
+  | Extension
+  | CompilerOptions
+  | Arch
+  | Language
+  | License
+  | BuildType
+  | Dependency
+  | VersionRange
+  | Version
+  | SPDX
+  | LicenseId
+  | LicenseExceptionId
+  | Scope
+  | Mixin
+  | ModuleRenaming
+  | ForeignLibOption
+  | ForeignLibType
+  | TestType
+  | Flag
+  deriving (Bounded, Enum, Eq, Ord, Read, Show)
+
+
+-- | A 'Benchmark' is a proper subrecord of an 'Executable', but we
+-- don't want to write 'Executable' in terms of 'Benchmark'! Hence,
+-- limit which types we will do the common-field extraction for to
+-- only 'BuildInfo', for the time being.
+isCandidateSubrecord :: KnownType -> Bool
+isCandidateSubrecord BuildInfo = True
+isCandidateSubrecord _ = False
+
+
+dhallType :: KnownType -> Dhall.Expr Dhall.Parser.Src a
+dhallType t = fmap Dhall.absurd
+  ( case t of
+      Config -> configRecordType
+      Library -> Dhall.expected library
+      ForeignLibrary -> Dhall.expected foreignLib
+      Executable -> Dhall.expected executable
+      Benchmark -> Dhall.expected benchmark
+      TestSuite -> Dhall.expected testSuite
+      SetupBuildInfo -> Dhall.expected setupBuildInfo
+      BuildInfo -> buildInfoType
+      SourceRepo -> Dhall.expected sourceRepo
+      RepoType -> Dhall.expected repoType
+      RepoKind -> Dhall.expected repoKind
+      Compiler -> Dhall.expected compilerFlavor
+      OS -> Dhall.expected operatingSystem
+      Extension -> Dhall.expected extension
+      CompilerOptions -> Dhall.expected compilerOptions
+      Arch -> Dhall.expected arch
+      Language -> Dhall.expected language
+      License -> Dhall.expected license
+      BuildType -> Dhall.expected buildType
+      Package -> Dhall.expected genericPackageDescription
+      Dependency -> Dhall.expected dependency
+      VersionRange -> Dhall.expected versionRange
+      Version -> Dhall.expected version
+      SPDX -> Dhall.expected spdxLicense
+      LicenseId -> Dhall.expected spdxLicenseId
+      LicenseExceptionId -> Dhall.expected spdxLicenseExceptionId
+      Scope -> Dhall.expected executableScope
+      Mixin -> Dhall.expected mixin
+      ModuleRenaming -> Dhall.expected moduleRenaming
+      ForeignLibOption -> Dhall.expected foreignLibOption
+      ForeignLibType -> Dhall.expected foreignLibType
+      TestType -> Dhall.expected testSuiteInterface
+      Flag -> Dhall.expected flag
+  )
+
+
+factored :: KnownType -> Expr.Expr Dhall.Parser.Src KnownType
+factored rootType =
+  sortExpr ( foldl' step ( dhallType rootType ) [ minBound .. maxBound ] )
+  where
+    step expr factorType =
+      fmap
+        ( fromMaybe factorType )
+        ( cse
+          ( isCandidateSubrecord factorType )
+          ( dhallType factorType )
+          expr
+        )
+
+
+-- | No variables should be free in the expression to lift.
+cse
+  :: ( Eq s, Eq a )
+  => Bool
+     -- ^ Should we attempt to find the subexpression as a sub-record?
+  -> Expr.Expr s a
+     -- ^ The common subexpression to lift.
+  -> Expr.Expr s a
+     -- ^ The expression to remove a common subexpression from.
+  -> Expr.Expr s ( Maybe a )
+     -- ^ 'Nothing' if it's representing a lifted subexpression.
+cse subrecord ( fmap Just -> body ) ( fmap Just -> expr ) =
+  case transformOf Dhall.subExpressions go expr of
+    -- Don't lift the whole thing out - it's not a win.
+    Expr.Embed Nothing ->
+      body
+    expr' ->
+      expr'
+
+  where
+
+    go e | e == body =
+      Expr.Embed Nothing
+
+    go e | subrecord, Just extra <- subtractRecordFields e body =
+      Expr.CombineTypes ( Expr.Embed Nothing ) extra
+
+    go e =
+      e
+
+
+subtractRecordFields
+  :: ( Eq s, Eq a )
+  => Expr.Expr s a
+  -> Expr.Expr s a
+  -> Maybe ( Expr.Expr s a )
+subtractRecordFields a b = do
+
+  Expr.Record left <-
+    return a
+
+  Expr.Record right <-
+    return b
+
+  let
+    intersection =
+      Map.intersectionWith (==) left right
+
+  -- The right record cannot have any fields not in left.
+  guard ( null ( Map.difference right left ) )
+
+  -- We must have at least one field with a common name
+  guard ( not ( null intersection ) )
+
+  -- All common fields must have identical types
+  guard ( and intersection )
+
+  let
+    extra =
+      Map.difference left right
+
+  guard ( not ( null extra ) )
+
+  return ( Expr.Record extra )
+
+
+chunkExprs
+  :: ( Applicative f )
+  => ( Expr.Expr s a -> f ( Expr.Expr t b ) )
+  -> Dhall.Chunks s a -> f ( Dhall.Chunks t b )
+chunkExprs f ( Dhall.Chunks chunks final ) =
+  flip Dhall.Chunks final <$> traverse ( traverse f ) chunks
+
+
+-- | The return value of this should be linted.
+mapWithBindings
+  :: ( ( Text -> Expr.Var ) -> a -> b )
+  -> Expr.Expr s a
+  -> Expr.Expr s b
+mapWithBindings f =
+  go UnorderedMap.empty
+
+  where
+
+    outermostVar bindings n =
+      Expr.V n ( fromMaybe 0 ( UnorderedMap.lookup n bindings ) )
+
+    shiftName =
+      UnorderedMap.alter ( Just . succ . fromMaybe 0 )
+
+    go bindings = \case
+      Expr.Lam n t b ->
+        Expr.Lam n
+          ( go bindings t )
+          ( go ( shiftName n bindings ) b )
+
+      Expr.Pi n t b ->
+        Expr.Pi n ( go bindings t ) ( go ( shiftName n bindings ) b )
+
+      Expr.App f a ->
+        Expr.App ( go bindings f ) ( go bindings a )
+
+      Expr.Let bs e ->
+        go' ( toList bs ) bindings
+          where
+            -- Since we lint afterwards, it's fine to transform one
+            -- let with many bindings into many lets with one
+            -- binding each.
+            go' ( Expr.Binding n t b : bs ) bindings' =
+              Expr.Let
+                ( pure
+                  ( Expr.Binding n
+                    ( fmap ( go bindings' ) t )
+                    ( go bindings' b )
+                  )
+                )
+                ( go' bs ( shiftName n bindings' ) )
+            go' [] bindings' =
+              go bindings' e
+
+      Expr.Annot a b ->
+        Expr.Annot ( go bindings a ) ( go bindings b )
+
+      Expr.BoolAnd a b ->
+        Expr.BoolAnd ( go bindings a ) ( go bindings b )
+
+      Expr.BoolOr a b ->
+        Expr.BoolOr ( go bindings a ) ( go bindings b )
+
+      Expr.BoolEQ a b ->
+        Expr.BoolEQ ( go bindings a ) ( go bindings b )
+
+      Expr.BoolNE a b ->
+        Expr.BoolNE ( go bindings a ) ( go bindings b )
+
+      Expr.BoolIf a b c ->
+        Expr.BoolIf ( go bindings a ) ( go bindings b ) ( go bindings c )
+
+      Expr.NaturalPlus a b ->
+        Expr.NaturalPlus ( go bindings a ) ( go bindings b )
+
+      Expr.NaturalTimes a b ->
+        Expr.NaturalTimes ( go bindings a ) ( go bindings b )
+
+      Expr.ListAppend a b ->
+        Expr.ListAppend ( go bindings a ) ( go bindings b )
+
+      Expr.Combine a b ->
+        Expr.Combine ( go bindings a ) ( go bindings b )
+
+      Expr.Prefer a b ->
+        Expr.Prefer ( go bindings a ) ( go bindings b )
+
+      Expr.TextAppend a b ->
+        Expr.TextAppend ( go bindings a ) ( go bindings b )
+
+      Expr.ListLit t elems ->
+        Expr.ListLit
+          ( fmap ( go bindings ) t )
+          ( fmap ( go bindings ) elems )
+
+      Expr.OptionalLit t elems ->
+        Expr.OptionalLit
+          ( go bindings t )
+          ( fmap ( go bindings ) elems )
+
+      Expr.Some a ->
+        Expr.Some ( go bindings a )
+
+      Expr.None ->
+        Expr.None
+
+      Expr.Record fields ->
+        Expr.Record ( fmap ( go bindings ) fields )
+
+      Expr.RecordLit fields ->
+        Expr.RecordLit ( fmap ( go bindings ) fields )
+
+      Expr.Union fields ->
+        Expr.Union ( fmap ( fmap ( go bindings ) ) fields )
+
+      Expr.UnionLit n a fields ->
+        Expr.UnionLit n ( go bindings a ) ( fmap ( fmap ( go bindings ) ) fields )
+
+      Expr.Merge a b t ->
+        Expr.Merge ( go bindings a ) ( go bindings b ) ( fmap ( go bindings ) t )
+
+      Expr.Field e f ->
+        Expr.Field ( go bindings e ) f
+
+      Expr.Note s e ->
+        Expr.Note s ( go bindings e )
+
+      Expr.CombineTypes a b ->
+        Expr.CombineTypes ( go bindings a ) ( go bindings b )
+
+      Expr.Project e fs ->
+        Expr.Project
+          ( go bindings e )
+          ( go bindings <$> fs )
+
+      Expr.ImportAlt l r ->
+        Expr.ImportAlt ( go bindings l ) ( go bindings r )
+
+      Expr.IntegerToDouble ->
+        Expr.IntegerToDouble
+
+      Expr.Embed a ->
+        Expr.Embed
+          ( f ( outermostVar bindings ) a )
+
+      Expr.Const c ->
+        Expr.Const c
+
+      Expr.Var v ->
+        Expr.Var v
+
+      Expr.Bool ->
+        Expr.Bool
+
+      Expr.BoolLit b ->
+        Expr.BoolLit b
+
+      Expr.Natural ->
+        Expr.Natural
+
+      Expr.NaturalLit n ->
+        Expr.NaturalLit n
+
+      Expr.NaturalFold ->
+        Expr.NaturalFold
+
+      Expr.NaturalBuild ->
+        Expr.NaturalBuild
+
+      Expr.NaturalIsZero ->
+        Expr.NaturalIsZero
+
+      Expr.NaturalEven ->
+        Expr.NaturalEven
+
+      Expr.NaturalOdd ->
+        Expr.NaturalOdd
+
+      Expr.NaturalToInteger ->
+        Expr.NaturalToInteger
+
+      Expr.NaturalShow ->
+        Expr.NaturalShow
+
+      Expr.Integer ->
+        Expr.Integer
+
+      Expr.IntegerShow ->
+        Expr.IntegerShow
+
+      Expr.IntegerLit n ->
+        Expr.IntegerLit n
+
+      Expr.Double ->
+        Expr.Double
+
+      Expr.DoubleShow ->
+        Expr.DoubleShow
+
+      Expr.DoubleLit n ->
+        Expr.DoubleLit n
+
+      Expr.Text ->
+        Expr.Text
+
+      Expr.TextLit t ->
+        Expr.TextLit ( over chunkExprs ( go bindings ) t )
+
+      Expr.TextShow ->
+        Expr.TextShow
+
+      Expr.List ->
+        Expr.List
+
+      Expr.ListBuild ->
+        Expr.ListBuild
+
+      Expr.ListFold ->
+        Expr.ListFold
+
+      Expr.ListLength ->
+        Expr.ListLength
+
+      Expr.ListHead ->
+        Expr.ListHead
+
+      Expr.ListLast ->
+        Expr.ListLast
+
+      Expr.ListIndexed ->
+        Expr.ListIndexed
+
+      Expr.ListReverse ->
+        Expr.ListReverse
+
+      Expr.Optional ->
+        Expr.Optional
+
+      Expr.OptionalFold ->
+        Expr.OptionalFold
+
+      Expr.OptionalBuild ->
+        Expr.OptionalBuild
diff --git a/meta/Main.hs b/meta/Main.hs
--- a/meta/Main.hs
+++ b/meta/Main.hs
@@ -18,6 +18,8 @@
 
 import CabalToDhall
   ( KnownDefault, PreludeReference (..), getDefault )
+import DhallToCabal.FactorType
+  ( KnownType (..), factored )
 import DhallToCabal.Util
   ( relativeTo )
 
@@ -25,6 +27,7 @@
 import qualified Data.Text.Prettyprint.Doc.Render.Text as Pretty
 import qualified Dhall.Core
 import qualified Dhall.Core as Expr ( Expr(..) )
+import qualified Dhall.Lint as Lint
 import qualified Dhall.Parser
 import qualified Options.Applicative as OptParse
 import qualified System.IO
@@ -51,12 +54,57 @@
 defaultFile typ = "./defaults" </> show typ <.> "dhall"
 
 
+typeFile :: KnownType -> FilePath
+typeFile = \case
+  Library -> "types/Library.dhall"
+  ForeignLibrary -> "types/ForeignLibrary.dhall"
+  Benchmark -> "types/Benchmark.dhall"
+  Executable -> "types/Executable.dhall"
+  TestSuite -> "types/TestSuite.dhall"
+  BuildInfo -> "types/BuildInfo.dhall"
+  Config -> "types/Config.dhall"
+  SourceRepo -> "types/SourceRepo.dhall"
+  RepoType -> "types/RepoType.dhall"
+  RepoKind -> "types/RepoKind.dhall"
+  Compiler -> "types/Compiler.dhall"
+  OS -> "types/OS.dhall"
+  Extension -> "types/Extension.dhall"
+  CompilerOptions -> "types/CompilerOptions.dhall"
+  Arch -> "types/Arch.dhall"
+  Language -> "types/Language.dhall"
+  License -> "types/License.dhall"
+  BuildType -> "types/BuildType.dhall"
+  Package -> "types/Package.dhall"
+  VersionRange -> "types/VersionRange.dhall"
+  Version -> "types/Version.dhall"
+  SPDX -> "types/SPDX.dhall"
+  LicenseId -> "types/SPDX/LicenseId.dhall"
+  LicenseExceptionId -> "types/SPDX/LicenseExceptionId.dhall"
+  Scope -> "types/Scope.dhall"
+  ModuleRenaming -> "types/ModuleRenaming.dhall"
+  ForeignLibOption -> "types/ForeignLibOption.dhall"
+  ForeignLibType -> "types/ForeignLibType.dhall"
+  SetupBuildInfo -> "types/SetupBuildInfo.dhall"
+  Dependency -> "types/Dependency.dhall"
+  TestType -> "types/TestType.dhall"
+  Mixin -> "types/Mixin.dhall"
+  Flag -> "types/Flag.dhall"
+
+
 importFile :: FilePath -> Dhall.Core.Import
 importFile ( splitFileName -> ( directory, filename ) ) =
   let
-    components =
+    rawComponents =
       fromString <$>
         splitDirectories ( dropTrailingPathSeparator directory )
+    ( components, relativity ) =
+      case rawComponents of
+        ".." : rest -> ( rest, Dhall.Core.Parent )
+        -- `splitFileName "foo"` produces (".", "foo"). It'd be OK to
+        -- leave the dot component in, but we might as well remove it
+        -- for neatness.
+        "." : rest -> ( rest, Dhall.Core.Here )
+        _ -> ( rawComponents, Dhall.Core.Here )
   in
     Dhall.Core.Import
       { Dhall.Core.importHashed =
@@ -65,7 +113,7 @@
                 Nothing
             , Dhall.Core.importType =
                 Dhall.Core.Local
-                  Dhall.Core.Here
+                  relativity
                   ( Dhall.Core.File
                      ( Dhall.Core.Directory ( reverse components ) )
                      ( fromString filename )
@@ -76,11 +124,48 @@
       }
 
 
+writeOutput :: FilePath -> Dhall.Core.Expr s Dhall.Core.Import -> IO ()
+writeOutput dest expr =
+  System.IO.withFile dest System.IO.WriteMode $ \ hnd -> do
+    System.IO.hPutStrLn hnd $
+         "-- This file is auto-generated by dhall-to-cabal-meta. Look but"
+      ++ " don't touch (unless you want your edits to be over-written)."
+    Pretty.renderIO
+      hnd
+      ( Pretty.layoutSmart prettyOpts
+          ( Pretty.pretty expr )
+      )
+    -- Pretty.renderIO doesn't give us a final newline, so add that ourselves.
+    System.IO.hPutStr hnd "\n"
+
+
 meta :: MetaOptions -> IO ()
 meta (MetaOptions {..}) = do
   putStrLn $
-    "Generating defaults underneath " ++ prefix ++ "."
+    "Generating defaults and types underneath " ++ prefix ++ "."
 
+  putStrLn "Generating types..."
+
+  for_ [ minBound .. maxBound ] $ \ knownType -> do
+    let
+      localDest =
+        typeFile knownType
+
+      expr =
+        importFile . relativeTo localDest . typeFile <$> factored knownType
+
+      dest =
+        prefix </> localDest
+
+    putStrLn $
+      "  Writing type for " ++ show knownType ++ " to " ++ dest ++ "."
+
+    createDirectoryIfMissing True ( takeDirectory dest )
+
+    writeOutput dest expr
+
+  putStrLn "Generating defaults..."
+
   for_ [ minBound .. maxBound ] $ \ defaultType -> do
     let localDest =
           defaultFile defaultType
@@ -115,15 +200,7 @@
 
     createDirectoryIfMissing True ( takeDirectory dest )
 
-    System.IO.withFile dest System.IO.WriteMode $ \ hnd -> do
-      System.IO.hPutStrLn hnd $
-           "-- This file is auto-generated by dhall-to-cabal-meta. Look but"
-        ++ " don't touch (unless you want your edits to be over-written)."
-      Pretty.renderIO
-        hnd
-        ( Pretty.layoutSmart prettyOpts
-            ( Pretty.pretty expr )
-        )
+    writeOutput dest ( Lint.lint expr )
 
 
 -- Shamelessly taken from dhall-format
