diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,118 @@
+1.25.0
+
+* Supports version 8.0.0 of the standard
+    * See: https://github.com/dhall-lang/dhall-lang/releases/tag/v8.0.0
+* BREAKING CHANGE: Remove support for old-style `List`-like `Optional` literals
+    * List-like `Optional` Literals (i.e. `[ 1 ] : Optional Natural`) are no
+      longer valid
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1002
+* BREAKING CHANGE: Add support for semi-semantic caching
+    * This change significantly improves the performance of imports
+    * This change also automatically caches imports without an integrity check
+    * This changes several types in `Dhall.Import` to support this new
+      feature
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1113
+* BREAKING CHANGE: Implement new Unicode braced escape sequence
+    * Escape sequences encoding surrogate pairs are no longer valid
+    * Instead, characters previously encoded as surrogate pairs can instead be
+      encoded as a braced sequence
+    * For example: "\uD834\uDD1E" must now be written as "\u{1D11E}"
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/987
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1104
+* BREAKING CHANGE: Make the type of extract richer:
+    * `Dhall.extract` can now return a detailed error instead of just a `Maybe`
+    * This is a breaking chnage because the type of `extract` changed
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1011
+* BREAKING CHANGE: Add support for importing expressions `as Location`
+    * This is a breaking change because a new `Location` constructor was added
+      to `ImportMode`
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1019
+* BREAKING CHANGE: Switch `Var` to use an `Int`
+    * This is a performance improvement, but also a breaking change since the
+      `Integer` in the `Var` constructor was changed to an `Int`
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1044
+* BREAKING CHANGE: Add new `toMap` keyword
+    * This is a breaking change to the API because a new `ToMap` constructor
+      was added to the `Expr` type
+    * This is also a technically breaking change to the language because `toMap`
+      is now a reserved keyword, although most code should be unaffected in
+      practice
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1041
+* BREAKING CHANGE: Sort the fields of a record projection during normalization
+    * This is a technically breaking change to the language because any
+      expressions with an uninterpreted record projection will have a different
+      semantic integrity check.  However, most could should be unaffected in
+      practice
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1111
+* BUG FIX: Fix substitution into record projection by type
+    * An expression like this one was being incorrectly rejected:
+      `let e = { a = 10, b = "Text" } let s = { a : Natural } in e.(s)`, which
+      this change fixes
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1012
+* BUG FIX: Reject record projection when there is a field type mismatch
+    * Record projection by type was previously not checking the expected
+      field types, which this change fixes
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1027
+* BUG FIX: Fix linting of unused let bindings
+    * Certain let bindings were not correctly detected as unused, which this
+      change fixes
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1001
+* BUG FIX: Fix `--file` option
+    * The `--file` option from the previous release did not work, due to not
+      computing relative paths correctly, which this change fixes
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1004
+* BUG FIX: Minor fix to `dhall diff`
+    * `dhall diff` was incorrectly displaying spurious differences for
+      identical lists that were function arguments, which this change fixes
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1006
+* BUG FIX: Allow `Sort` as type annotation
+    * This should have been implemented in the previous release as part of
+      supporting version 8.0.0 of the standard, but was missed
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1024
+* BUG FIX: `Dhall.Map`: Reflect original key ordering in `Ord` instance
+    * `Dhall.Map` now considers key order when comparing `Map`s, which it should
+      have done before, but didn't
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1050
+* BUG FIX: Consistently format multi-line strings
+    * The formatter now formats naked multi-line strings the same as nested
+      multi-line strings
+    * Specifically, naked multi-line strings can now be formatted on a single
+      (just like nested multi-line strings)
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1056
+* BUG FIX: Make `isNormalized` consistent with `normalize`
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1115
+* BUG FIX: Make `normalizeWithM` consistent with `normalize`
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1126
+* BUG FIX: Fix import alternatives to recover from type errors
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1152
+* Feature: Semi-semantic caching
+    * The Haskell implementation now implicitly caches *all* local imports, not
+      just imports frozen by integrity checks, so that you don't have to freeze
+      them when doing local development
+    * These cached imports are still correctly invalidated if they or any of
+      their dependencies change
+    * This new implicit cache is stored underneath `~/.cache/dhall-haskell` by
+      default
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1154
+* Feature: New `dhall text` subcommand
+    * This new subcommand supersedes the old `dhall-to-text` executable
+* Feature: Add `instance Lift (Expr s a)`
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1119
+* Fixes and improvements to error messages:
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1030
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1137
+* Fixes and improvements to tests:
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1155
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1159
+* Performance improvements
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1036
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1051
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1048
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1057
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1065
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1066
+    * See: https://github.com/dhall-lang/dhall-haskell/pull/1085
+
 1.24.0
 
 * Supports version 8.0.0 of the standard
diff --git a/benchmark/deep-nested-large-record/Main.hs b/benchmark/deep-nested-large-record/Main.hs
--- a/benchmark/deep-nested-large-record/Main.hs
+++ b/benchmark/deep-nested-large-record/Main.hs
@@ -1,13 +1,13 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Main (main) where
 
-import Criterion.Main (defaultMain)
+import Gauge(defaultMain)
 
-import qualified Criterion as Criterion
 import qualified Data.Sequence as Seq
 import qualified Dhall.Core as Core
 import qualified Dhall.Import as Import
 import qualified Dhall.TypeCheck as TypeCheck
+import qualified Gauge
 
 dhallPreludeImport :: Core.Import
 dhallPreludeImport = Core.Import
@@ -21,8 +21,8 @@
     }
   }
 
-issue412 :: Core.Expr s TypeCheck.X -> Criterion.Benchmarkable
-issue412 prelude = Criterion.whnf TypeCheck.typeOf expr
+issue412 :: Core.Expr s TypeCheck.X -> Gauge.Benchmarkable
+issue412 prelude = Gauge.whnf TypeCheck.typeOf expr
   where
     expr
       = Core.Let (pure (Core.Binding "prelude" Nothing prelude))
@@ -30,8 +30,8 @@
       $ Seq.replicate 5
       $ Core.Var (Core.V "prelude" 0) `Core.Field` "types" `Core.Field` "Little" `Core.Field` "Foo"
 
-unionPerformance :: Core.Expr s TypeCheck.X -> Criterion.Benchmarkable
-unionPerformance prelude = Criterion.whnf TypeCheck.typeOf expr
+unionPerformance :: Core.Expr s TypeCheck.X -> Gauge.Benchmarkable
+unionPerformance prelude = Gauge.whnf TypeCheck.typeOf expr
   where
     innerBinding =
         Core.Binding "big" Nothing
@@ -47,6 +47,6 @@
 main = do
   prelude <- Import.load (Core.Embed dhallPreludeImport)
   defaultMain
-    [ Criterion.bench "issue 412" (issue412 prelude)
-    , Criterion.bench "union performance" (unionPerformance prelude)
+    [ Gauge.bench "issue 412" (issue412 prelude)
+    , Gauge.bench "union performance" (unionPerformance prelude)
     ]
diff --git a/benchmark/deep-nested-large-record/prelude.dhall b/benchmark/deep-nested-large-record/prelude.dhall
--- a/benchmark/deep-nested-large-record/prelude.dhall
+++ b/benchmark/deep-nested-large-record/prelude.dhall
@@ -1,5 +1,5 @@
 { types =
-  { Big = constructors ./BigEnum.dhall
-  , Little = constructors ./LittleEnum.dhall
+  { Big = ./BigEnum.dhall
+  , Little = ./LittleEnum.dhall
   }
 }
diff --git a/benchmark/dhall-command/Main.hs b/benchmark/dhall-command/Main.hs
deleted file mode 100644
--- a/benchmark/dhall-command/Main.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-
-{-# LANGUAGE OverloadedStrings #-}
-
-module Main where
-
-import qualified Dhall.Main as Main
-import           Dhall.Binary (defaultStandardVersion)
-
-options :: Main.Options
-options = Main.Options
-  { Main.mode = Main.Default Nothing False False
-  , Main.explain = False
-  , Main.plain = False
-  , Main.ascii = False
-  , Main.standardVersion = defaultStandardVersion
-  }
-
-main :: IO ()
-main = do
-  Main.command options
diff --git a/benchmark/map/Main.hs b/benchmark/map/Main.hs
deleted file mode 100644
--- a/benchmark/map/Main.hs
+++ /dev/null
@@ -1,44 +0,0 @@
-{-# LANGUAGE BangPatterns     #-}
-{-# LANGUAGE TypeApplications #-}
-
-module Main where
-
-import Criterion.Main (defaultMain, bgroup, bench, whnf, nfIO)
-
-import qualified Criterion.Main as Criterion
-import qualified Dhall.Map as Map
-
-testData :: Integer -> Map.Map Integer Integer
-testData i = foldr (\j -> Map.insert j j) mempty [1 .. i]
-
-benchOrderedTraversal :: String -> Map.Map Integer Integer -> Criterion.Benchmark
-benchOrderedTraversal dataLabel mapData =
-    bgroup ("Ordered Traversals: " <> dataLabel)
-        [ bench "traverseWithKey" $
-            whnf (Map.traverseWithKey (\_ i -> pure @Maybe $ i ^ i)) mapData
-        , bench "traverseWithKey_" $
-            whnf (Map.traverseWithKey_ (\_ i -> pure @Maybe (i ^ i) *> pure ())) mapData
-        ]
-
-benchUnorderedTraversal :: String -> Map.Map Integer Integer -> Criterion.Benchmark
-benchUnorderedTraversal dataLabel mapData =
-    bgroup ("Unordered Traversals: " <> dataLabel)
-        [ bench "unorderedTraverseWithKey_" $
-            whnf (Map.unorderedTraverseWithKey_ (\_ i -> pure @Maybe (i ^ i) *> pure ())) mapData
-        ]
-
-main :: IO ()
-main = do
-    let !smallMap  = testData 10
-        !mediumMap = testData 1000
-        !largeMap  = testData 100000
-    defaultMain
-        [ benchOrderedTraversal   "small" smallMap
-        , benchUnorderedTraversal "small" smallMap
-
-        , benchOrderedTraversal   "medium" mediumMap
-        , benchUnorderedTraversal "medium" mediumMap
-
-        , benchOrderedTraversal   "large"  largeMap
-        , benchUnorderedTraversal "large"  largeMap
-        ]
diff --git a/benchmark/parser/Main.hs b/benchmark/parser/Main.hs
--- a/benchmark/parser/Main.hs
+++ b/benchmark/parser/Main.hs
@@ -4,18 +4,19 @@
 module Main where
 
 import Control.Monad (forM)
-import Criterion.Main (defaultMain, bgroup, bench, whnf, nfIO)
 import Data.Map (Map, foldrWithKey, singleton, unions)
 import Data.Monoid ((<>))
+import Gauge (defaultMain, bgroup, bench, whnf, nfIO)
 
 import System.Directory
 
 import qualified Codec.Serialise
-import qualified Criterion.Main as Criterion
+import qualified Gauge
 import qualified Data.ByteString.Lazy
 import qualified Data.Text as T
 import qualified Data.Text.IO as TIO
 import qualified Dhall.Binary
+import qualified Dhall.Core as Dhall
 import qualified Dhall.Parser as Dhall
 
 #if MIN_VERSION_directory(1,2,3)
@@ -38,7 +39,7 @@
 type PreludeFiles = Map FilePath T.Text
 
 loadPreludeFiles :: IO PreludeFiles
-loadPreludeFiles = loadDirectory "Prelude"
+loadPreludeFiles = loadDirectory "./dhall-lang/Prelude"
     where
         loadDirectory :: FilePath -> IO PreludeFiles
         loadDirectory dir =
@@ -55,24 +56,25 @@
         loadFile :: FilePath -> IO PreludeFiles
         loadFile path = singleton path <$> TIO.readFile path
 
-benchParser :: PreludeFiles -> Criterion.Benchmark
+benchParser :: PreludeFiles -> Gauge.Benchmark
 benchParser =
       bgroup "exprFromText"
     . foldrWithKey (\name expr -> (benchExprFromText name expr :)) []
 
-benchExprFromText :: String -> T.Text -> Criterion.Benchmark
+benchExprFromText :: String -> T.Text -> Gauge.Benchmark
 benchExprFromText name expr =
     bench name $ whnf (Dhall.exprFromText "(input)") expr
 
 benchExprFromBytes
-    :: String -> Data.ByteString.Lazy.ByteString -> Criterion.Benchmark
+    :: String -> Data.ByteString.Lazy.ByteString -> Gauge.Benchmark
 benchExprFromBytes name bytes = bench name (whnf f bytes)
   where
     f bytes = do
         term <- case Codec.Serialise.deserialiseOrFail bytes of
             Left  _    -> Nothing
             Right term -> return term
-        case Dhall.Binary.decodeExpression term of
+        case Dhall.Binary.decodeExpression term
+          :: Either Dhall.Binary.DecodingFailure (Dhall.Expr () Dhall.Import) of
             Left  _          -> Nothing
             Right expression -> return expression
 
diff --git a/dhall-lang/Prelude/JSON/Tagged b/dhall-lang/Prelude/JSON/Tagged
--- a/dhall-lang/Prelude/JSON/Tagged
+++ b/dhall-lang/Prelude/JSON/Tagged
@@ -62,6 +62,16 @@
 -}
 let Tagged
     : Type → Type
-    = λ(a : Type) → { field : Text, nesting : ./Nesting, contents : a }
+    =   λ ( a
+          : Type
+          )
+      → { field :
+            Text
+        , nesting :
+              ./Nesting sha256:6284802edd41d5d725aa1ec7687e614e21ad1be7e14dd10996bfa9625105c335
+            ? ./Nesting
+        , contents :
+            a
+        }
 
 in  Tagged
diff --git a/dhall-lang/Prelude/JSON/package.dhall b/dhall-lang/Prelude/JSON/package.dhall
--- a/dhall-lang/Prelude/JSON/package.dhall
+++ b/dhall-lang/Prelude/JSON/package.dhall
@@ -4,4 +4,25 @@
 , keyValue =
       ./keyValue sha256:a0a97199d280c4cce72ffcbbf93b7ceda0a569cf4d173ac98e0aaaa78034b98c
     ? ./keyValue
+, string =
+      ./string sha256:7a8ac435d30a96092d72889f3d48eabf7cba47ecf553fd6bc07a79fdf473e8d2
+    ? ./string
+, number =
+      ./number sha256:534745568065ae19d2b0fe1d09eeb071e9717d0f392187eb0bc95f386b018bec
+    ? ./number
+, object =
+      ./object sha256:a4e047cf157c3971b026b3942a87d474c85950d9b9654f8ebc8631740abf75a9
+    ? ./object
+, array =
+      ./array sha256:3a4c06cf135f4c80619e48c0808f6600d19782705bc59ee7c27cfc2e0f097eb7
+    ? ./array
+, bool =
+      ./bool sha256:018d29f030b45d642aba6bb81bf2c19a7bf183684612ce7a2c8afd2099783c48
+    ? ./bool
+, null =
+      ./null sha256:52c1d45ab2ca54875b444bfb1afdea497c8c9b0652e5044fafd8b16d97f4b78d
+    ? ./null
+, render =
+      ./render sha256:81f5a84efbb35211b1556838e86d17ed497912cf765bfa4ab76708b21e5371f1
+    ? ./render
 }
diff --git a/dhall-lang/Prelude/Text/package.dhall b/dhall-lang/Prelude/Text/package.dhall
--- a/dhall-lang/Prelude/Text/package.dhall
+++ b/dhall-lang/Prelude/Text/package.dhall
@@ -1,14 +1,14 @@
 { concat =
-      ./concat sha256:35e20d9403fbadb1a0061edb84e076ed56313709fa4bc8124d86ff54896f20ac
+      ./concat sha256:731265b0288e8a905ecff95c97333ee2db614c39d69f1514cb8eed9259745fc0
     ? ./concat
 , concatMap =
-      ./concatMap sha256:175d893ad7f2b2c05fff9e32f0d9cbadc7f5fce57945071508cf3603f8aa298e
+      ./concatMap sha256:7a0b0b99643de69d6f94ba49441cd0fa0507cbdfa8ace0295f16097af37e226f
     ? ./concatMap
 , concatMapSep =
-      ./concatMapSep sha256:46b81a9e211fb8278bf2793e75e8175fef79e0e3e478ef1016e3233ecc2ddfe6
+      ./concatMapSep sha256:c272aca80a607bc5963d1fcb38819e7e0d3e72ac4d02b1183b1afb6a91340840
     ? ./concatMapSep
 , concatSep =
-      ./concatSep sha256:d28e61f5057a240e857e09dba1b040fa3477bddb9659c5606c760852a9165890
+      ./concatSep sha256:e4401d69918c61b92a4c0288f7d60a6560ca99726138ed8ebc58dca2cd205e58
     ? ./concatSep
 , show =
       ./show sha256:c9dc5de3e5f32872dbda57166804865e5e80785abe358ff61f1d8ac45f1f4784
diff --git a/dhall-lang/Prelude/package.dhall b/dhall-lang/Prelude/package.dhall
--- a/dhall-lang/Prelude/package.dhall
+++ b/dhall-lang/Prelude/package.dhall
@@ -13,6 +13,9 @@
 , List =
       ./List/package.dhall sha256:108be3af5ebd465f7091039f2216c433e65ae5d25556a9a71786dd84d33ef49a
     ? ./List/package.dhall
+, Map =
+      ./Map/package.dhall sha256:07cc274220c8bdb2c1a0c2d00d90bc1447e73e0ad2e1d72b89773e923f77e71e
+    ? ./Map/package.dhall
 , Natural =
       ./Natural/package.dhall sha256:fe08155c3a04500df847ca94d013ecd3dfc73ab5c136109b2414fce3ec42b63a
     ? ./Natural/package.dhall
@@ -20,9 +23,12 @@
       ./Optional/package.dhall sha256:36a366af67a3c26cd5d196e095d3023f18953c5b5db3a03956fa554609e5442a
     ? ./Optional/package.dhall
 , JSON =
-      ./JSON/package.dhall sha256:7f0c25a292e5d34ddfbbf3f6d90505567382f95d822b04f5810745f81ab1ef35
+      ./JSON/package.dhall sha256:34a613c89df3f314c606a813f592d1a09fedb3e5f5e63fcc0ae9c88245e8bdad
     ? ./JSON/package.dhall
 , Text =
-      ./Text/package.dhall sha256:c8bc93456397476051dc674c180ddd5db098546861c8df24bda8284511d3305e
+      ./Text/package.dhall sha256:3b6ed813caf2388b91056d625c6b958b72009f85a6af262d4a7b935b18caf62b
     ? ./Text/package.dhall
+, XML =
+      ./XML/package.dhall sha256:abace25be73c3ba823abfba92bb3742a0454d521804a5d19ab5dfbf966473a5d
+    ? ./XML/package.dhall
 }
diff --git a/dhall-lang/tests/binary-decode/failure/unit/ApplyNoArgs.dhallb b/dhall-lang/tests/binary-decode/failure/unit/ApplyNoArgs.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/ApplyNoArgs.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/failure/unit/LambdaExplicitlyNamedUnderscore.dhallb b/dhall-lang/tests/binary-decode/failure/unit/LambdaExplicitlyNamedUnderscore.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/LambdaExplicitlyNamedUnderscore.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/failure/unit/ListOneWithAnnotation.dhallb b/dhall-lang/tests/binary-decode/failure/unit/ListOneWithAnnotation.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/ListOneWithAnnotation.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/failure/unit/NaturalNegativeOne.dhallb b/dhall-lang/tests/binary-decode/failure/unit/NaturalNegativeOne.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/failure/unit/NaturalNegativeOne.dhallb
@@ -0,0 +1,1 @@
+ 
diff --git a/dhall-lang/tests/binary-decode/failure/unit/OperatorOrTooFewArgs.dhallb b/dhall-lang/tests/binary-decode/failure/unit/OperatorOrTooFewArgs.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/OperatorOrTooFewArgs.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/failure/unit/OperatorOrTooManyArgs.dhallb b/dhall-lang/tests/binary-decode/failure/unit/OperatorOrTooManyArgs.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/OperatorOrTooManyArgs.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/failure/unit/OperatorUnknownOpcode.dhallb b/dhall-lang/tests/binary-decode/failure/unit/OperatorUnknownOpcode.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/OperatorUnknownOpcode.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/failure/unit/PiExplicitlyNamedUnderscore.dhallb b/dhall-lang/tests/binary-decode/failure/unit/PiExplicitlyNamedUnderscore.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/PiExplicitlyNamedUnderscore.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/failure/unit/VariableExplicitlyNamedUnderscore.dhallb b/dhall-lang/tests/binary-decode/failure/unit/VariableExplicitlyNamedUnderscore.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/failure/unit/VariableExplicitlyNamedUnderscore.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/AnnotationA.dhallb b/dhall-lang/tests/binary-decode/success/unit/AnnotationA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/AnnotationA.dhallb
@@ -0,0 +1,1 @@
+gNatural
diff --git a/dhall-lang/tests/binary-decode/success/unit/AnnotationB.dhall b/dhall-lang/tests/binary-decode/success/unit/AnnotationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/AnnotationB.dhall
@@ -0,0 +1,1 @@
+5 : Natural
diff --git a/dhall-lang/tests/binary-decode/success/unit/ApplicationA.dhallb b/dhall-lang/tests/binary-decode/success/unit/ApplicationA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/ApplicationA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/ApplicationB.dhall b/dhall-lang/tests/binary-decode/success/unit/ApplicationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/ApplicationB.dhall
@@ -0,0 +1,1 @@
+Natural/show _
diff --git a/dhall-lang/tests/binary-decode/success/unit/ApplicationMultipleA.dhallb b/dhall-lang/tests/binary-decode/success/unit/ApplicationMultipleA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/ApplicationMultipleA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/ApplicationMultipleB.dhall b/dhall-lang/tests/binary-decode/success/unit/ApplicationMultipleB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/ApplicationMultipleB.dhall
@@ -0,0 +1,1 @@
+f x y z
diff --git a/dhall-lang/tests/binary-decode/success/unit/BoolFalseA.dhallb b/dhall-lang/tests/binary-decode/success/unit/BoolFalseA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/BoolFalseA.dhallb
@@ -0,0 +1,1 @@
+ô
diff --git a/dhall-lang/tests/binary-decode/success/unit/BoolFalseB.dhall b/dhall-lang/tests/binary-decode/success/unit/BoolFalseB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/BoolFalseB.dhall
@@ -0,0 +1,1 @@
+False
diff --git a/dhall-lang/tests/binary-decode/success/unit/BoolIfA.dhallb b/dhall-lang/tests/binary-decode/success/unit/BoolIfA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/BoolIfA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/BoolIfB.dhall b/dhall-lang/tests/binary-decode/success/unit/BoolIfB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/BoolIfB.dhall
@@ -0,0 +1,1 @@
+if True then x else y
diff --git a/dhall-lang/tests/binary-decode/success/unit/BoolTrueA.dhallb b/dhall-lang/tests/binary-decode/success/unit/BoolTrueA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/BoolTrueA.dhallb
@@ -0,0 +1,1 @@
+õ
diff --git a/dhall-lang/tests/binary-decode/success/unit/BoolTrueB.dhall b/dhall-lang/tests/binary-decode/success/unit/BoolTrueB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/BoolTrueB.dhall
@@ -0,0 +1,1 @@
+True
diff --git a/dhall-lang/tests/binary-decode/success/unit/BuiltinNaturalShowA.dhallb b/dhall-lang/tests/binary-decode/success/unit/BuiltinNaturalShowA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/BuiltinNaturalShowA.dhallb
@@ -0,0 +1,1 @@
+lNatural/show
diff --git a/dhall-lang/tests/binary-decode/success/unit/BuiltinNaturalShowB.dhall b/dhall-lang/tests/binary-decode/success/unit/BuiltinNaturalShowB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/BuiltinNaturalShowB.dhall
@@ -0,0 +1,1 @@
+Natural/show
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleDoubleA.dhallb b/dhall-lang/tests/binary-decode/success/unit/DoubleDoubleA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/DoubleDoubleA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleDoubleB.dhall b/dhall-lang/tests/binary-decode/success/unit/DoubleDoubleB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/DoubleDoubleB.dhall
@@ -0,0 +1,6 @@
+{-
+The CBOR for this is an (oversized) double-precision number.  It could have been
+stored as single- (or even half-) precision, but we require decoders to accept
+it anyway.
+-}
+2.0
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleHalfA.dhallb b/dhall-lang/tests/binary-decode/success/unit/DoubleHalfA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/DoubleHalfA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleHalfB.dhall b/dhall-lang/tests/binary-decode/success/unit/DoubleHalfB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/DoubleHalfB.dhall
@@ -0,0 +1,7 @@
+{-
+The CBOR for this is a half-precision number.  Although dhall specifies that
+encoders should only use half-precision for infinities and NaN, it also
+specifies that decoders should tolerate floating point numbers with any
+precision.
+-}
+1.0
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleInfinityA.dhallb b/dhall-lang/tests/binary-decode/success/unit/DoubleInfinityA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/DoubleInfinityA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleInfinityB.dhall b/dhall-lang/tests/binary-decode/success/unit/DoubleInfinityB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/DoubleInfinityB.dhall
@@ -0,0 +1,1 @@
+Infinity
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleNegativeInfinityA.dhallb b/dhall-lang/tests/binary-decode/success/unit/DoubleNegativeInfinityA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/DoubleNegativeInfinityA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleNegativeInfinityB.dhall b/dhall-lang/tests/binary-decode/success/unit/DoubleNegativeInfinityB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/DoubleNegativeInfinityB.dhall
@@ -0,0 +1,1 @@
+-Infinity
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleSingleA.dhallb b/dhall-lang/tests/binary-decode/success/unit/DoubleSingleA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/DoubleSingleA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/DoubleSingleB.dhall b/dhall-lang/tests/binary-decode/success/unit/DoubleSingleB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/DoubleSingleB.dhall
@@ -0,0 +1,4 @@
+{-
+The CBOR for this is a single-precision number
+-}
+2.0
diff --git a/dhall-lang/tests/binary-decode/success/unit/IntegerNegativeOneA.dhallb b/dhall-lang/tests/binary-decode/success/unit/IntegerNegativeOneA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/IntegerNegativeOneA.dhallb
@@ -0,0 +1,1 @@
+ 
diff --git a/dhall-lang/tests/binary-decode/success/unit/IntegerNegativeOneB.dhall b/dhall-lang/tests/binary-decode/success/unit/IntegerNegativeOneB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/IntegerNegativeOneB.dhall
@@ -0,0 +1,1 @@
+-1
diff --git a/dhall-lang/tests/binary-decode/success/unit/IntegerZeroA.dhallb b/dhall-lang/tests/binary-decode/success/unit/IntegerZeroA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/IntegerZeroA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/IntegerZeroB.dhall b/dhall-lang/tests/binary-decode/success/unit/IntegerZeroB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/IntegerZeroB.dhall
@@ -0,0 +1,1 @@
++0
diff --git a/dhall-lang/tests/binary-decode/success/unit/LambdaNamedXA.dhallb b/dhall-lang/tests/binary-decode/success/unit/LambdaNamedXA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/LambdaNamedXA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/LambdaNamedXB.dhall b/dhall-lang/tests/binary-decode/success/unit/LambdaNamedXB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/LambdaNamedXB.dhall
@@ -0,0 +1,1 @@
+λ(x : _) → x
diff --git a/dhall-lang/tests/binary-decode/success/unit/LambdaUnderscoreA.dhallb b/dhall-lang/tests/binary-decode/success/unit/LambdaUnderscoreA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/LambdaUnderscoreA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/LambdaUnderscoreB.dhall b/dhall-lang/tests/binary-decode/success/unit/LambdaUnderscoreB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/LambdaUnderscoreB.dhall
@@ -0,0 +1,1 @@
+λ(_ : Natural) → _
diff --git a/dhall-lang/tests/binary-decode/success/unit/LetMultipleA.dhallb b/dhall-lang/tests/binary-decode/success/unit/LetMultipleA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/LetMultipleA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/LetMultipleB.dhall b/dhall-lang/tests/binary-decode/success/unit/LetMultipleB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/LetMultipleB.dhall
@@ -0,0 +1,1 @@
+let x : Natural = 1 let y = 2 in x
diff --git a/dhall-lang/tests/binary-decode/success/unit/LetOneTypedA.dhallb b/dhall-lang/tests/binary-decode/success/unit/LetOneTypedA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/LetOneTypedA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/LetOneTypedB.dhall b/dhall-lang/tests/binary-decode/success/unit/LetOneTypedB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/LetOneTypedB.dhall
@@ -0,0 +1,1 @@
+let x : Natural = 1 in x
diff --git a/dhall-lang/tests/binary-decode/success/unit/LetOneUntypedA.dhallb b/dhall-lang/tests/binary-decode/success/unit/LetOneUntypedA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/LetOneUntypedA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/LetOneUntypedB.dhall b/dhall-lang/tests/binary-decode/success/unit/LetOneUntypedB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/LetOneUntypedB.dhall
@@ -0,0 +1,1 @@
+let x = 1 in x
diff --git a/dhall-lang/tests/binary-decode/success/unit/ListEmptyA.dhallb b/dhall-lang/tests/binary-decode/success/unit/ListEmptyA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/ListEmptyA.dhallb
@@ -0,0 +1,1 @@
+gNatural
diff --git a/dhall-lang/tests/binary-decode/success/unit/ListEmptyB.dhall b/dhall-lang/tests/binary-decode/success/unit/ListEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/ListEmptyB.dhall
@@ -0,0 +1,1 @@
+[] : List Natural
diff --git a/dhall-lang/tests/binary-decode/success/unit/ListOneA.dhallb b/dhall-lang/tests/binary-decode/success/unit/ListOneA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/ListOneA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/ListOneB.dhall b/dhall-lang/tests/binary-decode/success/unit/ListOneB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/ListOneB.dhall
@@ -0,0 +1,1 @@
+[ _ ]
diff --git a/dhall-lang/tests/binary-decode/success/unit/MergeAnnotatedA.dhallb b/dhall-lang/tests/binary-decode/success/unit/MergeAnnotatedA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/MergeAnnotatedA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/MergeAnnotatedB.dhall b/dhall-lang/tests/binary-decode/success/unit/MergeAnnotatedB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/MergeAnnotatedB.dhall
@@ -0,0 +1,1 @@
+merge _ _@1 : _@2
diff --git a/dhall-lang/tests/binary-decode/success/unit/MergeUnannotatedA.dhallb b/dhall-lang/tests/binary-decode/success/unit/MergeUnannotatedA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/MergeUnannotatedA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/MergeUnannotatedB.dhall b/dhall-lang/tests/binary-decode/success/unit/MergeUnannotatedB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/MergeUnannotatedB.dhall
@@ -0,0 +1,1 @@
+merge _ _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/NaturalTwentyFourA.dhallb b/dhall-lang/tests/binary-decode/success/unit/NaturalTwentyFourA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/NaturalTwentyFourA.dhallb
@@ -0,0 +1,1 @@
+
diff --git a/dhall-lang/tests/binary-decode/success/unit/NaturalTwentyFourB.dhall b/dhall-lang/tests/binary-decode/success/unit/NaturalTwentyFourB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/NaturalTwentyFourB.dhall
@@ -0,0 +1,5 @@
+{-
+24 is the first natural number that has a different CBOR encoding, hence the
+separate test here.
+-}
+24
diff --git a/dhall-lang/tests/binary-decode/success/unit/NaturalZeroA.dhallb b/dhall-lang/tests/binary-decode/success/unit/NaturalZeroA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/NaturalZeroA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/NaturalZeroB.dhall b/dhall-lang/tests/binary-decode/success/unit/NaturalZeroB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/NaturalZeroB.dhall
@@ -0,0 +1,1 @@
+0
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorAndA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorAndA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorAndA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorAndB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorAndB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorAndB.dhall
@@ -0,0 +1,1 @@
+_ && _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorCombineA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorCombineB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineB.dhall
@@ -0,0 +1,1 @@
+_ ∧ _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorCombineTypesA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineTypesA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineTypesA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorCombineTypesB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineTypesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorCombineTypesB.dhall
@@ -0,0 +1,1 @@
+_ ⩓ _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorEqA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorEqA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorEqA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorEqB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorEqB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorEqB.dhall
@@ -0,0 +1,1 @@
+_ == _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorImportAltA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorImportAltA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorImportAltA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorImportAltB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorImportAltB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorImportAltB.dhall
@@ -0,0 +1,1 @@
+_ ? _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorListAppendA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorListAppendA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorListAppendA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorListAppendB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorListAppendB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorListAppendB.dhall
@@ -0,0 +1,1 @@
+_ # _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorNeqA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorNeqA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorNeqA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorNeqB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorNeqB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorNeqB.dhall
@@ -0,0 +1,1 @@
+_ != _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorOrA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorOrA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorOrA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorOrB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorOrB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorOrB.dhall
@@ -0,0 +1,1 @@
+_ || _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorPlusA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorPlusA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorPlusA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorPlusB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorPlusB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorPlusB.dhall
@@ -0,0 +1,1 @@
+_ + _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorPreferA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorPreferA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorPreferA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorPreferB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorPreferB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorPreferB.dhall
@@ -0,0 +1,1 @@
+_ ⫽ _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorTextAppendA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorTextAppendA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorTextAppendA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorTextAppendB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorTextAppendB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorTextAppendB.dhall
@@ -0,0 +1,1 @@
+_ ++ _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorTimesA.dhallb b/dhall-lang/tests/binary-decode/success/unit/OperatorTimesA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/OperatorTimesA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/OperatorTimesB.dhall b/dhall-lang/tests/binary-decode/success/unit/OperatorTimesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/OperatorTimesB.dhall
@@ -0,0 +1,1 @@
+_ * _@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/PiNamedXA.dhallb b/dhall-lang/tests/binary-decode/success/unit/PiNamedXA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/PiNamedXA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/PiNamedXB.dhall b/dhall-lang/tests/binary-decode/success/unit/PiNamedXB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/PiNamedXB.dhall
@@ -0,0 +1,1 @@
+∀(x : Natural) → _
diff --git a/dhall-lang/tests/binary-decode/success/unit/PiUnderscoreA.dhallb b/dhall-lang/tests/binary-decode/success/unit/PiUnderscoreA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/PiUnderscoreA.dhallb
@@ -0,0 +1,1 @@
+gNaturaldText
diff --git a/dhall-lang/tests/binary-decode/success/unit/PiUnderscoreB.dhall b/dhall-lang/tests/binary-decode/success/unit/PiUnderscoreB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/PiUnderscoreB.dhall
@@ -0,0 +1,1 @@
+Natural → Text
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordFieldAccessA.dhallb b/dhall-lang/tests/binary-decode/success/unit/RecordFieldAccessA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/RecordFieldAccessA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordFieldAccessB.dhall b/dhall-lang/tests/binary-decode/success/unit/RecordFieldAccessB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/RecordFieldAccessB.dhall
@@ -0,0 +1,1 @@
+_.x
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordLiteralA.dhallb b/dhall-lang/tests/binary-decode/success/unit/RecordLiteralA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/RecordLiteralA.dhallb
@@ -0,0 +1,1 @@
+¢axgNaturalaydBool
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordLiteralB.dhall b/dhall-lang/tests/binary-decode/success/unit/RecordLiteralB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/RecordLiteralB.dhall
@@ -0,0 +1,1 @@
+{ x = Natural, y = Bool }
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordProjectFieldsA.dhallb b/dhall-lang/tests/binary-decode/success/unit/RecordProjectFieldsA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/RecordProjectFieldsA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordProjectFieldsB.dhall b/dhall-lang/tests/binary-decode/success/unit/RecordProjectFieldsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/RecordProjectFieldsB.dhall
@@ -0,0 +1,1 @@
+r.{ x, y, z }
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordTypeA.dhallb b/dhall-lang/tests/binary-decode/success/unit/RecordTypeA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/RecordTypeA.dhallb
@@ -0,0 +1,1 @@
+¢axgNaturalaydBool
diff --git a/dhall-lang/tests/binary-decode/success/unit/RecordTypeB.dhall b/dhall-lang/tests/binary-decode/success/unit/RecordTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/RecordTypeB.dhall
@@ -0,0 +1,1 @@
+{ x : Natural, y : Bool }
diff --git a/dhall-lang/tests/binary-decode/success/unit/SomeA.dhallb b/dhall-lang/tests/binary-decode/success/unit/SomeA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/SomeA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/SomeB.dhall b/dhall-lang/tests/binary-decode/success/unit/SomeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/SomeB.dhall
@@ -0,0 +1,1 @@
+Some _
diff --git a/dhall-lang/tests/binary-decode/success/unit/TextInterpolatedA.dhallb b/dhall-lang/tests/binary-decode/success/unit/TextInterpolatedA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/TextInterpolatedA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/TextInterpolatedB.dhall b/dhall-lang/tests/binary-decode/success/unit/TextInterpolatedB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/TextInterpolatedB.dhall
@@ -0,0 +1,1 @@
+"foo${_}bar"
diff --git a/dhall-lang/tests/binary-decode/success/unit/TextSimpleA.dhallb b/dhall-lang/tests/binary-decode/success/unit/TextSimpleA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/TextSimpleA.dhallb
@@ -0,0 +1,1 @@
+cfoo
diff --git a/dhall-lang/tests/binary-decode/success/unit/TextSimpleB.dhall b/dhall-lang/tests/binary-decode/success/unit/TextSimpleB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/TextSimpleB.dhall
@@ -0,0 +1,1 @@
+"foo"
diff --git a/dhall-lang/tests/binary-decode/success/unit/UnionTypeA.dhallb b/dhall-lang/tests/binary-decode/success/unit/UnionTypeA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/UnionTypeA.dhallb
@@ -0,0 +1,1 @@
+¢axgNaturalayö
diff --git a/dhall-lang/tests/binary-decode/success/unit/UnionTypeB.dhall b/dhall-lang/tests/binary-decode/success/unit/UnionTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/UnionTypeB.dhall
@@ -0,0 +1,1 @@
+< x : Natural | y >
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableNamedA.dhallb b/dhall-lang/tests/binary-decode/success/unit/VariableNamedA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/VariableNamedA.dhallb
@@ -0,0 +1,1 @@
+ax
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableNamedB.dhall b/dhall-lang/tests/binary-decode/success/unit/VariableNamedB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/VariableNamedB.dhall
@@ -0,0 +1,1 @@
+x@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableNamedOversizedIntA.dhallb b/dhall-lang/tests/binary-decode/success/unit/VariableNamedOversizedIntA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/VariableNamedOversizedIntA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableNamedOversizedIntB.dhall b/dhall-lang/tests/binary-decode/success/unit/VariableNamedOversizedIntB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/VariableNamedOversizedIntB.dhall
@@ -0,0 +1,16 @@
+{-
+
+This file tests that a number encoded as a type larger than required is still
+read successfully.  In this case, the .dhallb file has the bytes:
+
+    8261 781b 0000 0000 0000 0001
+
+which means the array ["x", uint64(1)].  This could have been stored in fewer
+bytes as
+
+    8261 7801
+
+but decoders are required to allow the oversized representation.
+
+-}
+x@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreA.dhallb b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreA.dhallb
@@ -0,0 +1,1 @@
+
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreB.dhall b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreB.dhall
@@ -0,0 +1,1 @@
+_@4
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreOversizedIntA.dhallb b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreOversizedIntA.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreOversizedIntA.dhallb differ
diff --git a/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreOversizedIntB.dhall b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreOversizedIntB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/VariableUnderscoreOversizedIntB.dhall
@@ -0,0 +1,13 @@
+{-
+
+This file tests that a number encoded as a type larger than required is still
+read successfully.  In this case, the .dhallb file has the bytes:
+
+    1b00 0000 0000 0000 01
+
+which means a uint64 with value 1.  The number 1 could have been stored in a
+single byte as 01, but decoders are required to allow the oversized
+representation.
+
+-}
+_@1
diff --git a/dhall-lang/tests/binary-decode/success/unit/recordProjectionByExpressionA.dhallb b/dhall-lang/tests/binary-decode/success/unit/recordProjectionByExpressionA.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/recordProjectionByExpressionA.dhallb
@@ -0,0 +1,2 @@
+
+¢aaab¡aagNatural
diff --git a/dhall-lang/tests/binary-decode/success/unit/recordProjectionByExpressionB.dhall b/dhall-lang/tests/binary-decode/success/unit/recordProjectionByExpressionB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/binary-decode/success/unit/recordProjectionByExpressionB.dhall
@@ -0,0 +1,1 @@
+{ a = 1, b = 2 }.({ a : Natural })
diff --git a/dhall-lang/tests/import/cache/dhall/1220efc43103e49b56c5bf089db8e0365bbfc455b8a2f0dc6ee5727a3586f85969fd b/dhall-lang/tests/import/cache/dhall/1220efc43103e49b56c5bf089db8e0365bbfc455b8a2f0dc6ee5727a3586f85969fd
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/import/cache/dhall/1220efc43103e49b56c5bf089db8e0365bbfc455b8a2f0dc6ee5727a3586f85969fd differ
diff --git a/dhall-lang/tests/import/data/simpleLocation.dhall b/dhall-lang/tests/import/data/simpleLocation.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/import/data/simpleLocation.dhall
@@ -0,0 +1,1 @@
+./simple.dhall as Location
diff --git a/dhall-lang/tests/import/success/asLocationA.dhall b/dhall-lang/tests/import/success/asLocationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/import/success/asLocationA.dhall
@@ -0,0 +1,8 @@
+{ _1 = ./some/import.dhall as Location
+, _2 = ../data/simpleLocation.dhall
+, _3 = /absolute/import sha256:f9340badf94a684e652e0a384f64363293d8b632d971f3453f7ee22f10ab6e75 as Location
+, _4 = https://prelude.dhall-lang.org/package.dhall as Location
+, _5 = env:HOME as Location
+, _6 = missing as Location
+, _7 = (missing as Location) ? 42  -- `missing` fails as an import, but definitely resolves as Location
+}
diff --git a/dhall-lang/tests/import/success/asLocationB.dhall b/dhall-lang/tests/import/success/asLocationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/import/success/asLocationB.dhall
@@ -0,0 +1,8 @@
+{ _1 = < Local : Text | Remote : Text | Environment : Text | Missing >.Local "./some/import.dhall"
+, _2 = < Local : Text | Remote : Text | Environment : Text | Missing >.Local "../data/simple.dhall"
+, _3 = < Local : Text | Remote : Text | Environment : Text | Missing >.Local "/absolute/import"
+, _4 = < Local : Text | Remote : Text | Environment : Text | Missing >.Remote "https://prelude.dhall-lang.org/package.dhall"
+, _5 = < Local : Text | Remote : Text | Environment : Text | Missing >.Environment "env:HOME"
+, _6 = < Local : Text | Remote : Text | Environment : Text | Missing >.Missing
+, _7 = < Local : Text | Remote : Text | Environment : Text | Missing >.Missing
+}
diff --git a/dhall-lang/tests/import/success/customHeadersA.dhall b/dhall-lang/tests/import/success/customHeadersA.dhall
--- a/dhall-lang/tests/import/success/customHeadersA.dhall
+++ b/dhall-lang/tests/import/success/customHeadersA.dhall
@@ -1,3 +1,3 @@
 https://httpbin.org/user-agent
-  using [ { header = "User-Agent", value = "Dhall" } ]
+  using [ { mapKey = "User-Agent", mapValue = "Dhall" } ]
   as Text
diff --git a/dhall-lang/tests/import/success/hashFromCacheA.dhall b/dhall-lang/tests/import/success/hashFromCacheA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/import/success/hashFromCacheA.dhall
@@ -0,0 +1,11 @@
+{-
+An import with an integrity check MUST be fetched from the cache if possible.
+
+If you have set XDG_CACHE_HOME as described in tests/README.md, this hash will
+be available in the cache, so even though this is a `missing` import, it can be
+resolved by its hash.
+
+(If you're interested, the value is a fully-αβ-normalized copy of
+Prelude/Optional/null at time of test creation)
+-}
+missing sha256:efc43103e49b56c5bf089db8e0365bbfc455b8a2f0dc6ee5727a3586f85969fd
diff --git a/dhall-lang/tests/import/success/hashFromCacheB.dhall b/dhall-lang/tests/import/success/hashFromCacheB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/import/success/hashFromCacheB.dhall
@@ -0,0 +1,3 @@
+  λ(_ : Type)
+→ λ(_ : Optional _)
+→ Optional/fold _@1 _ Bool (λ(_ : _@1) → False) True
diff --git a/dhall-lang/tests/import/success/headerForwardingA.dhall b/dhall-lang/tests/import/success/headerForwardingA.dhall
--- a/dhall-lang/tests/import/success/headerForwardingA.dhall
+++ b/dhall-lang/tests/import/success/headerForwardingA.dhall
@@ -13,4 +13,4 @@
    Prelude changes (sorry)
 -}
 https://test.dhall-lang.org/Bool/package.dhall
-  using [ { header = "Test", value = "Example" } ]
+  using [ { mapKey = "Test", mapValue = "Example" } ]
diff --git a/dhall-lang/tests/import/success/noHeaderForwardingA.dhall b/dhall-lang/tests/import/success/noHeaderForwardingA.dhall
--- a/dhall-lang/tests/import/success/noHeaderForwardingA.dhall
+++ b/dhall-lang/tests/import/success/noHeaderForwardingA.dhall
@@ -3,4 +3,4 @@
    https://httpbin.org/user-agent
 -}
 https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/tests/import/success/customHeadersA.dhall
-  using [ { header = "User-Agent", value = "Secret" } ]
+  using [ { mapKey = "User-Agent", mapValue = "Secret" } ]
diff --git a/dhall-lang/tests/normalization/success/simple/equalNoCommuteA.dhall b/dhall-lang/tests/normalization/success/simple/equalNoCommuteA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/equalNoCommuteA.dhall
@@ -0,0 +1,1 @@
+λ(x : Bool) → x == False
diff --git a/dhall-lang/tests/normalization/success/simple/equalNoCommuteB.dhall b/dhall-lang/tests/normalization/success/simple/equalNoCommuteB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/equalNoCommuteB.dhall
@@ -0,0 +1,1 @@
+λ(x : Bool) → x == False
diff --git a/dhall-lang/tests/normalization/success/simple/notEqualNoCommuteA.dhall b/dhall-lang/tests/normalization/success/simple/notEqualNoCommuteA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/notEqualNoCommuteA.dhall
@@ -0,0 +1,1 @@
+λ(x : Bool) → x != True
diff --git a/dhall-lang/tests/normalization/success/simple/notEqualNoCommuteB.dhall b/dhall-lang/tests/normalization/success/simple/notEqualNoCommuteB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/notEqualNoCommuteB.dhall
@@ -0,0 +1,1 @@
+λ(x : Bool) → x != True
diff --git a/dhall-lang/tests/normalization/success/simple/plusNoCommuteA.dhall b/dhall-lang/tests/normalization/success/simple/plusNoCommuteA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/plusNoCommuteA.dhall
@@ -0,0 +1,1 @@
+λ(x : Natural) → x + 1
diff --git a/dhall-lang/tests/normalization/success/simple/plusNoCommuteB.dhall b/dhall-lang/tests/normalization/success/simple/plusNoCommuteB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/plusNoCommuteB.dhall
@@ -0,0 +1,1 @@
+λ(x : Natural) → x + 1
diff --git a/dhall-lang/tests/normalization/success/simple/timesNoCommuteA.dhall b/dhall-lang/tests/normalization/success/simple/timesNoCommuteA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/timesNoCommuteA.dhall
@@ -0,0 +1,1 @@
+λ(x : Natural) → x * 2
diff --git a/dhall-lang/tests/normalization/success/simple/timesNoCommuteB.dhall b/dhall-lang/tests/normalization/success/simple/timesNoCommuteB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/simple/timesNoCommuteB.dhall
@@ -0,0 +1,1 @@
+λ(x : Natural) → x * 2
diff --git a/dhall-lang/tests/normalization/success/unit/EmptyToMapA.dhall b/dhall-lang/tests/normalization/success/unit/EmptyToMapA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/EmptyToMapA.dhall
@@ -0,0 +1,1 @@
+toMap {=} : List {mapKey : Text, mapValue : Natural}
diff --git a/dhall-lang/tests/normalization/success/unit/EmptyToMapB.dhall b/dhall-lang/tests/normalization/success/unit/EmptyToMapB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/EmptyToMapB.dhall
@@ -0,0 +1,1 @@
+[] : List { mapKey : Text, mapValue : Natural }
diff --git a/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationA.dhall b/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationA.dhall
--- a/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationA.dhall
+++ b/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationA.dhall
@@ -1,1 +1,1 @@
-[] : List (if True then x else y)
+[] : { x = List Bool }.x
diff --git a/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationB.dhall b/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationB.dhall
--- a/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationB.dhall
+++ b/dhall-lang/tests/normalization/success/unit/ListNormalizeTypeAnnotationB.dhall
@@ -1,1 +1,1 @@
-[] : List x
+[] : List Bool
diff --git a/dhall-lang/tests/normalization/success/unit/NoneNaturalA.dhall b/dhall-lang/tests/normalization/success/unit/NoneNaturalA.dhall
deleted file mode 100644
--- a/dhall-lang/tests/normalization/success/unit/NoneNaturalA.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-[] : Optional Natural
diff --git a/dhall-lang/tests/normalization/success/unit/NoneNaturalB.dhall b/dhall-lang/tests/normalization/success/unit/NoneNaturalB.dhall
deleted file mode 100644
--- a/dhall-lang/tests/normalization/success/unit/NoneNaturalB.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-None Natural
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeEmptyA.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeEmptyA.dhall
@@ -0,0 +1,1 @@
+{ a = 10, b = Some 10 }.({})
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeEmptyB.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeEmptyB.dhall
@@ -0,0 +1,1 @@
+{=}
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNonEmptyA.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNonEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNonEmptyA.dhall
@@ -0,0 +1,1 @@
+{ a = 10, b = Some 10, c = [ "text" ]}.({ a : Natural, b : Optional Natural })
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNonEmptyB.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNonEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNonEmptyB.dhall
@@ -0,0 +1,1 @@
+{ a = 10, b = Some 10 }
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNormalizeProjectionA.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNormalizeProjectionA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNormalizeProjectionA.dhall
@@ -0,0 +1,1 @@
+x.({ a : Natural, b : Natural })
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNormalizeProjectionB.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNormalizeProjectionB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionByTypeNormalizeProjectionB.dhall
@@ -0,0 +1,1 @@
+x.{ a, b }
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionNormalizeFieldsA.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionNormalizeFieldsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionNormalizeFieldsA.dhall
@@ -0,0 +1,1 @@
+λ(x : { a : Bool, b : Bool, c : Bool }) → x.{ c, a }
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionNormalizeFieldsB.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionNormalizeFieldsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/RecordProjectionNormalizeFieldsB.dhall
@@ -0,0 +1,1 @@
+λ(x : { a : Bool, b : Bool, c : Bool }) → x.{ a, c }
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeEmptyA.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeEmptyA.dhall
deleted file mode 100644
--- a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeEmptyA.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-{ a = 10, b = Some 10 }.({})
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeEmptyB.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeEmptyB.dhall
deleted file mode 100644
--- a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeEmptyB.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-{=}
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeNonEmptyA.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeNonEmptyA.dhall
deleted file mode 100644
--- a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeNonEmptyA.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-{ a = 10, b = Some 10, c = [ "text" ]}.({ a : Natural, b : Optional Natural })
diff --git a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeNonEmptyB.dhall b/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeNonEmptyB.dhall
deleted file mode 100644
--- a/dhall-lang/tests/normalization/success/unit/RecordProjectionTypeNonEmptyB.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-{ a = 10, b = Some 10 }
diff --git a/dhall-lang/tests/normalization/success/unit/ToMapA.dhall b/dhall-lang/tests/normalization/success/unit/ToMapA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/ToMapA.dhall
@@ -0,0 +1,1 @@
+toMap { foo= 1, bar= 4, baz= 9 }
diff --git a/dhall-lang/tests/normalization/success/unit/ToMapB.dhall b/dhall-lang/tests/normalization/success/unit/ToMapB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/ToMapB.dhall
@@ -0,0 +1,4 @@
+[ { mapKey = "bar", mapValue = 4 }
+, { mapKey = "baz", mapValue = 9 }
+, { mapKey = "foo", mapValue = 1 }
+]
diff --git a/dhall-lang/tests/normalization/success/unit/ToMapWithTypeA.dhall b/dhall-lang/tests/normalization/success/unit/ToMapWithTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/ToMapWithTypeA.dhall
@@ -0,0 +1,16 @@
+{- This test verifies that the normalized result does not contain a type
+   annotation if the input record is non-empty.
+
+   The reason for this test is that implementations might need to store
+   optional type annotations for both `toMap` invocations and empty list
+   literals.  If an implementation does so, a common mistake is to take
+   the optional type annotation and always pass it through as the optional
+   type annotation for the list literal that `toMap` generates.
+
+   However, the standard specifies that `toMap` should only generate a
+   type annotation if the input record is empty.  If the input record is
+   non-empty then the resulting normalized list is non-empty and therefore
+   should not have a type annotation (since a non-empty list with a type
+   annotation is not in normal form).
+-}
+toMap { foo= 1, bar= 4, baz= 9 } : List { mapKey : Text, mapValue : Natural }
diff --git a/dhall-lang/tests/normalization/success/unit/ToMapWithTypeB.dhall b/dhall-lang/tests/normalization/success/unit/ToMapWithTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/normalization/success/unit/ToMapWithTypeB.dhall
@@ -0,0 +1,4 @@
+[ { mapKey = "bar", mapValue = 4 }
+, { mapKey = "baz", mapValue = 9 }
+, { mapKey = "foo", mapValue = 1 }
+]
diff --git a/dhall-lang/tests/parser/failure/nonCharacter.dhall b/dhall-lang/tests/parser/failure/nonCharacter.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/failure/nonCharacter.dhall
@@ -0,0 +1,1 @@
+"\u{10FFFF}"
diff --git a/dhall-lang/tests/parser/failure/nonUtf8.dhall b/dhall-lang/tests/parser/failure/nonUtf8.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/failure/nonUtf8.dhall
@@ -0,0 +1,4 @@
+{- This test verifies that an implementation correctly rejects non-UTF8
+   characters, such as this one: "í "
+-}
+1
diff --git a/dhall-lang/tests/parser/failure/urlWithoutPath.dhall b/dhall-lang/tests/parser/failure/urlWithoutPath.dhall
deleted file mode 100644
--- a/dhall-lang/tests/parser/failure/urlWithoutPath.dhall
+++ /dev/null
@@ -1,2 +0,0 @@
--- See issue #205 for why this is not permitted
-http://example.com/
diff --git a/dhall-lang/tests/parser/success/RecordProjectionByType.dhall b/dhall-lang/tests/parser/success/RecordProjectionByType.dhall
deleted file mode 100644
--- a/dhall-lang/tests/parser/success/RecordProjectionByType.dhall
+++ /dev/null
@@ -1,15 +0,0 @@
-let example1 = λ(A : Type) → λ(B : Type) → λ(C : { x : A, y : B }) → C.({ x : A }) : { x : A }
-
-let example2 =
-	  λ(A : Type)
-	→ λ(B : Type)
-	→ λ(C : { p : A, q : B })
-	→ C.(let r = { p : A } in r) : { p : A }
-
-let A = Natural
-
-let B = Text
-
-in
-
-(example1 A B { x = 10, y = "Text" }) ∧ (example2 A B { p = 10, q = "Text" }) : { x : A, p : A }
diff --git a/dhall-lang/tests/parser/success/annotationsA.dhall b/dhall-lang/tests/parser/success/annotationsA.dhall
--- a/dhall-lang/tests/parser/success/annotationsA.dhall
+++ b/dhall-lang/tests/parser/success/annotationsA.dhall
@@ -1,4 +1,1 @@
-{ foo = ([] : List Natural) # [1, 2, 3] # ([1, 2, 3] : List Natural)
-, bar = [] : Optional Natural
-, baz = [1] : Optional Natural
-} : { foo : List Natural, bar : Optional Natural, baz : Optional Natural }
+([] : List Natural) # [ 1, 2, 3 ] # ([ 1, 2, 3 ] : List Natural) : List Natural
diff --git a/dhall-lang/tests/parser/success/annotationsB.dhallb b/dhall-lang/tests/parser/success/annotationsB.dhallb
Binary files a/dhall-lang/tests/parser/success/annotationsB.dhallb and b/dhall-lang/tests/parser/success/annotationsB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/recordProjectionByExpressionA.dhall b/dhall-lang/tests/parser/success/recordProjectionByExpressionA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/recordProjectionByExpressionA.dhall
@@ -0,0 +1,15 @@
+let example1 = λ(A : Type) → λ(B : Type) → λ(C : { x : A, y : B }) → C.({ x : A }) : { x : A }
+
+let example2 =
+	  λ(A : Type)
+	→ λ(B : Type)
+	→ λ(C : { p : A, q : B })
+	→ C.(let r = { p : A } in r) : { p : A }
+
+let A = Natural
+
+let B = Text
+
+in
+
+(example1 A B { x = 10, y = "Text" }) ∧ (example2 A B { p = 10, q = "Text" }) : { x : A, p : A }
diff --git a/dhall-lang/tests/parser/success/recordProjectionByExpressionB.dhallb b/dhall-lang/tests/parser/success/recordProjectionByExpressionB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/recordProjectionByExpressionB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringA.dhall b/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringA.dhall
--- a/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringA.dhall
+++ b/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringA.dhall
@@ -1,1 +1,1 @@
-"\\\"\$\\\/\b\f\n\r\t \u2200(a : Type) \u2192 a"
+"\\\"\$\\\/\b\f\n\r\t\u{1D11E} \u2200(a : Type) \u2192 a"
diff --git a/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringB.dhallb b/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringB.dhallb
--- a/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringB.dhallb
+++ b/dhall-lang/tests/parser/success/text/escapedDoubleQuotedStringB.dhallb
@@ -1,2 +1,2 @@
-x\"$\/
-	 â(a : Type) â a
+x"\"$\/
+	ð â(a : Type) â a
diff --git a/dhall-lang/tests/parser/success/toMapA.dhall b/dhall-lang/tests/parser/success/toMapA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/toMapA.dhall
@@ -0,0 +1,1 @@
+toMap { foo= 1, bar= 4, baz= 9 }
diff --git a/dhall-lang/tests/parser/success/toMapB.dhallb b/dhall-lang/tests/parser/success/toMapB.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/toMapB.dhallb
@@ -0,0 +1,1 @@
+£cbarcbaz	cfoo
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmpty1A.dhall b/dhall-lang/tests/parser/success/unit/ListLitEmpty1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/ListLitEmpty1A.dhall
@@ -0,0 +1,1 @@
+[] : List T
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmpty1B.dhallb b/dhall-lang/tests/parser/success/unit/ListLitEmpty1B.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/ListLitEmpty1B.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmpty2A.dhall b/dhall-lang/tests/parser/success/unit/ListLitEmpty2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/ListLitEmpty2A.dhall
@@ -0,0 +1,1 @@
+[] : T
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmpty2B.dhallb b/dhall-lang/tests/parser/success/unit/ListLitEmpty2B.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/ListLitEmpty2B.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmptyA.dhall b/dhall-lang/tests/parser/success/unit/ListLitEmptyA.dhall
deleted file mode 100644
--- a/dhall-lang/tests/parser/success/unit/ListLitEmptyA.dhall
+++ /dev/null
@@ -1,1 +0,0 @@
-[] : List T
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmptyB.dhallb b/dhall-lang/tests/parser/success/unit/ListLitEmptyB.dhallb
deleted file mode 100644
Binary files a/dhall-lang/tests/parser/success/unit/ListLitEmptyB.dhallb and /dev/null differ
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmptyPrecedenceA.dhall b/dhall-lang/tests/parser/success/unit/ListLitEmptyPrecedenceA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/ListLitEmptyPrecedenceA.dhall
@@ -0,0 +1,1 @@
+[] : List T U
diff --git a/dhall-lang/tests/parser/success/unit/ListLitEmptyPrecedenceB.dhallb b/dhall-lang/tests/parser/success/unit/ListLitEmptyPrecedenceB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/ListLitEmptyPrecedenceB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/RecordFieldAccessA.dhall b/dhall-lang/tests/parser/success/unit/RecordFieldAccessA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/RecordFieldAccessA.dhall
@@ -0,0 +1,1 @@
+r.x
diff --git a/dhall-lang/tests/parser/success/unit/RecordFieldAccessB.dhallb b/dhall-lang/tests/parser/success/unit/RecordFieldAccessB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/RecordFieldAccessB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/RecordProjectFieldsA.dhall b/dhall-lang/tests/parser/success/unit/RecordProjectFieldsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/RecordProjectFieldsA.dhall
@@ -0,0 +1,1 @@
+r.{ x, y, z }
diff --git a/dhall-lang/tests/parser/success/unit/RecordProjectFieldsB.dhallb b/dhall-lang/tests/parser/success/unit/RecordProjectFieldsB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/RecordProjectFieldsB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeA.dhall b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeA.dhall
@@ -0,0 +1,1 @@
+{ a = 1, b = 2 }.({ a : Natural })
diff --git a/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeB.dhallb b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeB.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeB.dhallb
@@ -0,0 +1,2 @@
+
+¢aaab¡aagNatural
diff --git a/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeEmptyA.dhall b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeEmptyA.dhall
@@ -0,0 +1,1 @@
+{ a = 1, b = 2 }.({})
diff --git a/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeEmptyB.dhallb b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeEmptyB.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/RecordProjectionByTypeEmptyB.dhallb
@@ -0,0 +1,2 @@
+
+¢aaab 
diff --git a/dhall-lang/tests/parser/success/unit/import/asLocationA.dhall b/dhall-lang/tests/parser/success/unit/import/asLocationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/asLocationA.dhall
@@ -0,0 +1,6 @@
+{ _1 = ./some/import.dhall as Location
+, _2 = /absolute/import sha256:f9340badf94a684e652e0a384f64363293d8b632d971f3453f7ee22f10ab6e75 as Location
+, _3 = https://prelude.dhall-lang.org/package.dhall as Location
+, _4 = env:HOME as Location
+, _5 = missing as Location
+}
diff --git a/dhall-lang/tests/parser/success/unit/import/asLocationB.dhallb b/dhall-lang/tests/parser/success/unit/import/asLocationB.dhallb
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/asLocationB.dhallb
@@ -0,0 +1,2 @@
+¥b_1ödsomelimport.dhallb_2X" ù4­ùJhNe.
+8Od62Ø¶2ÙqóE?~â/«nuhabsolutefimportb_3öövprelude.dhall-lang.orgmpackage.dhallöb_4ödHOMEb_5ö
diff --git a/dhall-lang/tests/parser/success/unit/import/inlineUsingA.dhall b/dhall-lang/tests/parser/success/unit/import/inlineUsingA.dhall
--- a/dhall-lang/tests/parser/success/unit/import/inlineUsingA.dhall
+++ b/dhall-lang/tests/parser/success/unit/import/inlineUsingA.dhall
@@ -8,7 +8,7 @@
 -}
 
 https://example.com/foo using
-    [ { header = "Authorization"
-      , value  = "token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4"
+    [ { mapKey   = "Authorization"
+      , mapValue = "token 5199831f4dd3b79e7c5b7e0ebe75d67aa66e79d4"
       }
     ]
diff --git a/dhall-lang/tests/parser/success/unit/import/inlineUsingB.dhallb b/dhall-lang/tests/parser/success/unit/import/inlineUsingB.dhallb
Binary files a/dhall-lang/tests/parser/success/unit/import/inlineUsingB.dhallb and b/dhall-lang/tests/parser/success/unit/import/inlineUsingB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/parenthesizeUsingB.dhallb b/dhall-lang/tests/parser/success/unit/import/parenthesizeUsingB.dhallb
Binary files a/dhall-lang/tests/parser/success/unit/import/parenthesizeUsingB.dhallb and b/dhall-lang/tests/parser/success/unit/import/parenthesizeUsingB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/basicHttpA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpA.dhall
@@ -0,0 +1,1 @@
+http://example.com/someFile.dhall
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/basicHttpB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/basicHttpsA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpsA.dhall
@@ -0,0 +1,1 @@
+https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/basicHttpsB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpsB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/basicHttpsB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyPath0A.dhall b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath0A.dhall
@@ -0,0 +1,3 @@
+-- the normal form for an empty path is a path of `/`
+-- see RFC7230 section 2.7.3
+https://example.com
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyPath0B.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath0B.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath0B.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyPath1A.dhall b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath1A.dhall
@@ -0,0 +1,1 @@
+https://example.com/
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyPath1B.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath1B.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/emptyPath1B.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyPathSegmentA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/emptyPathSegmentA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/emptyPathSegmentA.dhall
@@ -0,0 +1,2 @@
+-- empty path segments are not equivalent to missing path segments
+https://example.com/foo//bar
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyPathSegmentB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/emptyPathSegmentB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/emptyPathSegmentB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyQueryA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/emptyQueryA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/emptyQueryA.dhall
@@ -0,0 +1,3 @@
+-- an empty query string is not the same as a missing one
+-- see RFC 3986 section 6.2.3
+https://example.com/foo?
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/emptyQueryB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/emptyQueryB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/emptyQueryB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/escapedPathA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/escapedPathA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/escapedPathA.dhall
@@ -0,0 +1,1 @@
+https://example.com/a%20b/c%2fd/e+f
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/escapedPathB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/escapedPathB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/escapedPathB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/escapedQueryA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/escapedQueryA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/escapedQueryA.dhall
@@ -0,0 +1,2 @@
+-- Unlike paths, query strings should *not* be decoded
+https://example.com/foo?a%20b=c%2fd&e+f/?
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/escapedQueryB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/escapedQueryB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/escapedQueryB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/fragmentParsesAsListAppendA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/fragmentParsesAsListAppendA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/fragmentParsesAsListAppendA.dhall
@@ -0,0 +1,11 @@
+{- Fragment identifiers are not allowed in URLs because they serve no purpose
+   for Dhall and they could lead to ambiguity if a parser interprets them as
+   the list append operator (`#`)
+
+   The following expression therefore only has one valid parse, which is to
+   interpret the `#` as a list append.  In other words, the following expression
+   is parsed as:
+
+   (https://example.com/foo) # bar
+-}
+https://example.com/foo#bar
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/fragmentParsesAsListAppendB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/fragmentParsesAsListAppendB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/fragmentParsesAsListAppendB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv4A.dhall b/dhall-lang/tests/parser/success/unit/import/urls/ipv4A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/ipv4A.dhall
@@ -0,0 +1,1 @@
+https://127.0.0.1/index.dhall
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv4B.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/ipv4B.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/ipv4B.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6longA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/ipv6longA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/ipv6longA.dhall
@@ -0,0 +1,1 @@
+https://[2001:db8:85a3:0:0:8a2e:370:7334]/tutorial.dhall
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6longB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/ipv6longB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/ipv6longB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6mediumA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/ipv6mediumA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/ipv6mediumA.dhall
@@ -0,0 +1,1 @@
+https://[2001:db8:85a3::8a2e:370:7334]/tutorial.dhall
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6mediumB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/ipv6mediumB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/ipv6mediumB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6shortA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/ipv6shortA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/ipv6shortA.dhall
@@ -0,0 +1,1 @@
+https://[::]/index.dhall
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6shortB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/ipv6shortB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/ipv6shortB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6withipv4A.dhall b/dhall-lang/tests/parser/success/unit/import/urls/ipv6withipv4A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/ipv6withipv4A.dhall
@@ -0,0 +1,1 @@
+https://[2001:db8:85a3::8a2e:3.112.115.52]/tutorial.dhall
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/ipv6withipv4B.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/ipv6withipv4B.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/ipv6withipv4B.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/portA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/portA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/portA.dhall
@@ -0,0 +1,1 @@
+https://example.com:1234/foo
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/portB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/portB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/portB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/potPourriA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/potPourriA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/potPourriA.dhall
@@ -0,0 +1,1 @@
+https://-._~%2C!$&'*+;=:@0abc1--12a------a-a--a-0/foo?/-._~%2C!$&'*+;=:@/?
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/potPourriB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/potPourriB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/potPourriB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/quotedPathFakeUrlEncodeA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/quotedPathFakeUrlEncodeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/quotedPathFakeUrlEncodeA.dhall
@@ -0,0 +1,1 @@
+https://example.com/"a%20b"/c
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/quotedPathFakeUrlEncodeB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/quotedPathFakeUrlEncodeB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/quotedPathFakeUrlEncodeB.dhallb differ
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/userinfoA.dhall b/dhall-lang/tests/parser/success/unit/import/urls/userinfoA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/parser/success/unit/import/urls/userinfoA.dhall
@@ -0,0 +1,1 @@
+https://john:doe@example.com/foo
diff --git a/dhall-lang/tests/parser/success/unit/import/urls/userinfoB.dhallb b/dhall-lang/tests/parser/success/unit/import/urls/userinfoB.dhallb
new file mode 100644
Binary files /dev/null and b/dhall-lang/tests/parser/success/unit/import/urls/userinfoB.dhallb differ
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/0A.dhall b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/0A.dhall
@@ -0,0 +1,1 @@
+{ x = "foo" }.x
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/0B.hash b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/0B.hash
@@ -0,0 +1,1 @@
+sha256:fd881e3d48cbc85b4044697bdce8167a77f3fb4665ff0b647a1210a17528818a
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/1A.dhall b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/1A.dhall
@@ -0,0 +1,1 @@
+< Foo : Text | Bar : Natural >.Foo
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/1B.hash b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/1B.hash
@@ -0,0 +1,1 @@
+sha256:35ba8bddad36acfb29cc6f1700364544c145e96cf522f592f18f90c2f96e6e9c
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/0A.dhall b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/0A.dhall
@@ -0,0 +1,1 @@
+{ foo : { bar : Text } } ⩓ { foo : { baz : Bool }, qux : Integer }
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/0B.hash b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/0B.hash
@@ -0,0 +1,1 @@
+sha256:4495bbf3fea6c579c65ee51463b225747803b94668013528b9ca0384304cb350
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/1A.dhall b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/1A.dhall
@@ -0,0 +1,1 @@
+{ foo : { bar : Text } } //\\ { foo : { baz : Bool }, qux : Integer }
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/1B.hash b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/1B.hash
@@ -0,0 +1,1 @@
+sha256:4495bbf3fea6c579c65ee51463b225747803b94668013528b9ca0384304cb350
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/0A.dhall b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/0A.dhall
@@ -0,0 +1,1 @@
+{ foo = 1, bar = "ABC" } // { baz = True }
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/0B.hash b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/0B.hash
@@ -0,0 +1,1 @@
+sha256:18fbad84131d28b6d412e6bc09d5ee8eaf432b639c6cc6f1bef1b4ef366642d7
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/0A.dhall b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/0A.dhall
@@ -0,0 +1,1 @@
+{ x = 1, y = True, z = "ABC" }.{ x, y }
diff --git a/dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/0B.hash b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/0B.hash
@@ -0,0 +1,1 @@
+sha256:b7ae500723633be94b53b905384236fcd18cbce59314bb51044bb7d3f3939e6e
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/and [ True, False, True ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/and ([] : List Bool)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/and/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/0A.dhall
@@ -0,0 +1,2 @@
+../../../../../../Prelude/Bool/build 
+(λ(bool : Type) → λ(true : bool) → λ(false : bool) → true)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/0B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/1A.dhall
@@ -0,0 +1,2 @@
+../../../../../../Prelude/Bool/build 
+(λ(bool : Type) → λ(true : bool) → λ(false : bool) → false)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/build/1B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/even [ False, True, False ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/0B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/even [ False, True ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/1B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/2A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/2A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/even [ False ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/2B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/2B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/2B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/3A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/3A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/3A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/even ([] : List Bool)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/3B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/3B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/even/3B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/fold True Natural 0 1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/0B.hash
@@ -0,0 +1,1 @@
+sha256:d0a48d41dcc572839faa9499b6c316b8d0d7115efac2ac770f062cd5fd6d0181
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/fold False Natural 0 1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/1B.hash
@@ -0,0 +1,1 @@
+sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/not True
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/not False
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/not/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/odd [ True, False, True ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/odd [ True, False ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/2A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/2A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/odd [ True ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/2B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/2B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/2B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/3A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/3A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/3A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/odd ([] : List Bool)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/3B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/3B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/3B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/or [ True, False, True ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/0B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/or ([] : List Bool)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/or/1B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/show True
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/0B.hash
@@ -0,0 +1,1 @@
+sha256:cdbb7fd982ba05e709fe67b23818fffb65ff9c0b8abde1abb5ac1a8245891a3f
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Bool/show False
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Bool/show/1B.hash
@@ -0,0 +1,1 @@
+sha256:2357bb42b56beb99180d967177c0490061bd3536a147dfe144d2630f983004e5
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Double/show/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Double/show -3.1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Double/show/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/0B.hash
@@ -0,0 +1,1 @@
+sha256:d0b3cdb4f3cd5375495629450de676f445c0da6980a5ada251df199079d0ef89
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Double/show/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Double/show 0.4
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Double/show/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Double/show/1B.hash
@@ -0,0 +1,1 @@
+sha256:dd5e8756d06f12ef6e003dc769b6e1222e5aa0c8491579dc65b2dd6f4f0ec53d
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Integer/show -3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/0B.hash
@@ -0,0 +1,1 @@
+sha256:053de6c2b7f281dd82dd9b643cc848e64633b88c9c57082ec0ac41257c5592d7
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Integer/show +0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/show/1B.hash
@@ -0,0 +1,1 @@
+sha256:4ab1c5aa262f39cdbe1f4924accb69bb44e2a616b5a0ca39655eebd0cc863d33
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Integer/toDouble -3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/0B.hash
@@ -0,0 +1,1 @@
+sha256:17bf67209e1b636613b9f97ca5f2b8a59fab70eaab65a029be19129ec5f444ad
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Integer/toDouble +2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/1B.hash
@@ -0,0 +1,1 @@
+sha256:d4b8848ff8a5d403469e4107f4a0cfdc42197bb3a9fcd2fa477b65dd252ce770
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/all/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/all/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/all/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/all Natural Natural/even [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/all/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/all/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/all/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/all/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/all/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/all/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/all Natural Natural/even ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/all/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/all/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/all/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/any/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/any/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/any/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/any Natural Natural/even [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/any/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/any/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/any/0B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/any/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/any/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/any/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/any Natural Natural/even ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/any/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/any/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/any/1B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/build/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/build/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/build/0A.dhall
@@ -0,0 +1,7 @@
+../../../../../../Prelude/List/build
+Text
+( λ(list : Type)
+→ λ(cons : Text → list → list)
+→ λ(nil : list)
+→ cons "ABC" (cons "DEF" nil)
+)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/build/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/build/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/build/0B.hash
@@ -0,0 +1,1 @@
+sha256:8a08646db7447b14cf03a497635242b6fb76365017fc1699b2a8807272a2ec58
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/build/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/build/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/build/1A.dhall
@@ -0,0 +1,7 @@
+../../../../../../Prelude/List/build
+Text
+( λ(list : Type)
+→ λ(cons : Text → list → list)
+→ λ(nil : list)
+→ nil
+)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/build/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/build/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/build/1B.hash
@@ -0,0 +1,1 @@
+sha256:6da0c98142d1168ac9d6080b7853d2b59b97d42079e1b2f121daf449b3b2e449
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concat/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/0A.dhall
@@ -0,0 +1,5 @@
+../../../../../../Prelude/List/concat Natural
+[ [ 0, 1, 2 ]
+, [ 3, 4 ]
+, [ 5, 6, 7, 8 ]
+]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concat/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/0B.hash
@@ -0,0 +1,1 @@
+sha256:ae3a399d05bafcee80c088ca6c740f0c61c2a4c6fce6d7e4f4fa26560eac083f
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concat/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/1A.dhall
@@ -0,0 +1,5 @@
+../../../../../../Prelude/List/concat Natural
+[ [] : List Natural
+, [] : List Natural
+, [] : List Natural
+]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concat/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concat/1B.hash
@@ -0,0 +1,1 @@
+sha256:d79a2e0e14809ab2dbd2d180e60da8e129a5fb197bdd0caed57e3828402e48a9
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/concatMap Natural Natural (λ(n : Natural) → [ n, n ]) [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/0B.hash
@@ -0,0 +1,1 @@
+sha256:ea1f5046bcba6efb41bde23738d9a3f3aa2cda3dc722999d9e4f477eb9d1b12d
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/concatMap Natural Natural (λ(n : Natural) → [ n, n ]) ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/1B.hash
@@ -0,0 +1,1 @@
+sha256:d79a2e0e14809ab2dbd2d180e60da8e129a5fb197bdd0caed57e3828402e48a9
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/filter/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/filter Natural Natural/even [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/filter/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/0B.hash
@@ -0,0 +1,1 @@
+sha256:988afbf26d86e95a4e6b78b0c344c9f987dcd5f7d7a7c541601bb1e9a01c9102
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/filter/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/filter Natural Natural/odd [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/filter/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/filter/1B.hash
@@ -0,0 +1,1 @@
+sha256:68248e1b69b249e6aeebcd36e7034f159b75d2931105fb4d41fc55d644b7ee8d
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/fold/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/0A.dhall
@@ -0,0 +1,6 @@
+../../../../../../Prelude/List/fold
+Natural
+[ 2, 3, 5 ]
+Natural
+(λ(x : Natural) → λ(y : Natural) → x + y)
+0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/fold/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/0B.hash
@@ -0,0 +1,1 @@
+sha256:d4428061b6fd69b6b45574603ac71060f9756a5c9a1642df322812e3a7ea858b
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/fold/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/1A.dhall
@@ -0,0 +1,7 @@
+  λ(nil : Natural)
+→ ../../../../../../Prelude/List/fold
+  Natural
+  [ 2, 3, 5 ]
+  Natural
+  (λ(x : Natural) → λ(y : Natural) → x + y)
+  nil
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/fold/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/1B.hash
@@ -0,0 +1,1 @@
+sha256:3bd732ae873cb107c3dfef4ae953f6480af310b27fda6b678b43569841e1012b
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/fold/2A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/2A.dhall
@@ -0,0 +1,4 @@
+  λ(list : Type)
+→ λ(cons : Natural → list → list)
+→ λ(nil : list)
+→ ../../../../../../Prelude/List/fold Natural [ 2, 3, 5 ] list cons nil
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/fold/2B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/2B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/fold/2B.hash
@@ -0,0 +1,1 @@
+sha256:186c485f65cbbf97aba127a1205a16ee1125ec279e727ff05d8f8ada624257c7
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/generate/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/generate 5 Bool Natural/even
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/generate/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/0B.hash
@@ -0,0 +1,1 @@
+sha256:0860d18cdc28bc4e7b081bf8c9891ab07e303e4f72b3439bb3a92d7dedd7e8fa
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/generate/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/generate 0 Bool Natural/even
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/generate/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/generate/1B.hash
@@ -0,0 +1,1 @@
+sha256:fb7841e1b3b3e3511196b38df96e73ac7785edef663ea450f97b3c1322742d69
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/head/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/head/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/head/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/head Natural [ 0, 1, 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/head/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/head/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/head/0B.hash
@@ -0,0 +1,1 @@
+sha256:7ac81b06d39113c93f17cc3d2aa55867453f68f35f58a4d577f5bacbb969225f
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/head/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/head/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/head/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/head Natural ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/head/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/head/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/head/1B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/indexed Bool [ True, False, True ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/0B.hash
@@ -0,0 +1,1 @@
+sha256:7225a557384940f3f2f7f40e5e3e36384be7892c08a24e5467d0f75cd0dda9e1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/indexed Bool ([] : List Bool)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/indexed/1B.hash
@@ -0,0 +1,1 @@
+sha256:ff10538dd6bf3bf736bf38f54575e1ddd251aa1d6aad7c6ff6f4159bfc87cc4a
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/iterate 10 Natural (λ(x : Natural) → x * 2) 1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/0B.hash
@@ -0,0 +1,1 @@
+sha256:577ea4905798b4fbd4803b3a7c82902594f0c3e5d84af46cec4514da9801760a
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/iterate 0 Natural (λ(x : Natural) → x * 2) 1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/iterate/1B.hash
@@ -0,0 +1,1 @@
+sha256:d79a2e0e14809ab2dbd2d180e60da8e129a5fb197bdd0caed57e3828402e48a9
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/last/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/last/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/last/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/last Natural [ 0, 1, 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/last/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/last/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/last/0B.hash
@@ -0,0 +1,1 @@
+sha256:1400a0fa058e1c8fc58926856000d82d99fad204bf48e36f54dccaafc0acd3a0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/last/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/last/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/last/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/last Natural ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/last/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/last/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/last/1B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/length/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/length/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/length/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/length Natural [ 0, 1, 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/length/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/length/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/length/0B.hash
@@ -0,0 +1,1 @@
+sha256:15f52ecf91c94c1baac02d5a4964b2ed8fa401641a2c8a95e8306ec7c1e3b8d2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/length/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/length/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/length/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/length Natural ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/length/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/length/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/length/1B.hash
@@ -0,0 +1,1 @@
+sha256:d0a48d41dcc572839faa9499b6c316b8d0d7115efac2ac770f062cd5fd6d0181
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/map/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/map/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/map/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/map Natural Bool Natural/even [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/map/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/map/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/map/0B.hash
@@ -0,0 +1,1 @@
+sha256:aaf20d96e69473f836397764ec61b64a65dc5084ee33692ecd8b85ca63962827
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/map/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/map/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/map/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/map Natural Bool Natural/even ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/map/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/map/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/map/1B.hash
@@ -0,0 +1,1 @@
+sha256:fb7841e1b3b3e3511196b38df96e73ac7785edef663ea450f97b3c1322742d69
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/null/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/null/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/null/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/null Natural [ 0, 1, 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/null/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/null/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/null/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/null/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/null/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/null/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/null Natural ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/null/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/null/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/null/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/replicate 9 Natural 1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/0B.hash
@@ -0,0 +1,1 @@
+sha256:4241079fe7c8a46be37831a35dedb60645c3a86a2ab24778e03c27c820813cdc
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/replicate 0 Natural 1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/replicate/1B.hash
@@ -0,0 +1,1 @@
+sha256:d79a2e0e14809ab2dbd2d180e60da8e129a5fb197bdd0caed57e3828402e48a9
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/reverse Natural [ 0, 1, 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/0B.hash
@@ -0,0 +1,1 @@
+sha256:ed586af4895ad8118e77976c6ee2fef5a39f69eb9378df32349c81fee2d61d4d
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/reverse Natural ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/reverse/1B.hash
@@ -0,0 +1,1 @@
+sha256:d79a2e0e14809ab2dbd2d180e60da8e129a5fb197bdd0caed57e3828402e48a9
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/0A.dhall
@@ -0,0 +1,15 @@
+../../../../../../Prelude/List/shifted
+Bool
+[ [ { index = 0, value = True }
+  , { index = 1, value = True }
+  , { index = 2, value = False }
+  ]
+, [ { index = 0, value = False }
+  , { index = 1, value = False }
+  ]
+, [ { index = 0, value = True }
+  , { index = 1, value = True }
+  , { index = 2, value = True }
+  , { index = 3, value = True }
+  ]
+]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/0B.hash
@@ -0,0 +1,1 @@
+sha256:e5c27411e9b0692ac96e6914420f17748eda33d3854067675c6a3b3780fb15a6
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/shifted Bool ([] : List (List { index : Natural, value : Bool }))
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/shifted/1B.hash
@@ -0,0 +1,1 @@
+sha256:ff10538dd6bf3bf736bf38f54575e1ddd251aa1d6aad7c6ff6f4159bfc87cc4a
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/0A.dhall
@@ -0,0 +1,7 @@
+../../../../../../Prelude/List/unzip
+Text
+Bool
+[ { _1 = "ABC", _2 = True }
+, { _1 = "DEF", _2 = False }
+, { _1 = "GHI", _2 = True }
+]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/0B.hash
@@ -0,0 +1,1 @@
+sha256:fffb0db166d08b99a1beb1650a3c9ee6e7bf6ce1054150bea4dae9b8ce5e4fbf
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/List/unzip Text Bool ([] : List { _1 : Text, _2 : Bool })
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/List/unzip/1B.hash
@@ -0,0 +1,1 @@
+sha256:c598ac5f10c7d20fe201f94ed5d9d18a847e89c65db9478492c930e5c42af710
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/0A.dhall
@@ -0,0 +1,6 @@
+../../../../../../Prelude/Natural/build
+( λ(natural : Type)
+→ λ(succ : natural → natural)
+→ λ(zero : natural)
+→ succ (succ (succ zero))
+)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/0B.hash
@@ -0,0 +1,1 @@
+sha256:15f52ecf91c94c1baac02d5a4964b2ed8fa401641a2c8a95e8306ec7c1e3b8d2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/1A.dhall
@@ -0,0 +1,6 @@
+../../../../../../Prelude/Natural/build
+( λ(natural : Type)
+→ λ(succ : natural → natural)
+→ λ(zero : natural)
+→ zero
+)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/build/1B.hash
@@ -0,0 +1,1 @@
+sha256:d0a48d41dcc572839faa9499b6c316b8d0d7115efac2ac770f062cd5fd6d0181
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/enumerate 10
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/0B.hash
@@ -0,0 +1,1 @@
+sha256:a7af1902b53cc68a9613b659ebf58d06c840072625d14f9fde7eefa0e0f2add0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/enumerate 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/1B.hash
@@ -0,0 +1,1 @@
+sha256:d79a2e0e14809ab2dbd2d180e60da8e129a5fb197bdd0caed57e3828402e48a9
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/even 3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/even 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/even/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/fold 3 Natural (λ(x : Natural) → 5 * x) 1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/0B.hash
@@ -0,0 +1,1 @@
+sha256:39b0b6e0e0b616af4f20c7b06fefcd26dcdbd5be772d885fe73487574ec8b56b
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/1A.dhall
@@ -0,0 +1,1 @@
+λ(zero : Natural) → ../../../../../../Prelude/Natural/fold 3 Natural (λ(x : Natural) → 5 * x) zero
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/1B.hash
@@ -0,0 +1,1 @@
+sha256:c6ddb630f2abfcd78aa874079323ba2a9f80f548c5687346a0fcedff78d14f52
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/2A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/2A.dhall
@@ -0,0 +1,4 @@
+  λ(natural : Type)
+→ λ(succ : natural → natural)
+→ λ(zero : natural)
+→ ../../../../../../Prelude/Natural/fold 3 natural succ zero
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/2B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/2B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/2B.hash
@@ -0,0 +1,1 @@
+sha256:08681d6fb9b1bb78be37f5a976186daa8fc39d0d1d3f7dbb716dc33937a6746d
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/isZero 2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/isZero 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/odd 3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/0B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/odd 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/1B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/product [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/0B.hash
@@ -0,0 +1,1 @@
+sha256:49036d5d861d35d4aff797975a3447ebae71a9057951a03fc810ab8e8e00a498
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/product ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/product/1B.hash
@@ -0,0 +1,1 @@
+sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/show 3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/0B.hash
@@ -0,0 +1,1 @@
+sha256:c8911e8084484ca0d468334986a19f0a11cc18b31f41f272e7d4ad2cd479ad39
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/show 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/show/1B.hash
@@ -0,0 +1,1 @@
+sha256:e3062e0493964ebaef8880667ad8a6289c277618c0b31141610abf5d69e7c9ea
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/sum [ 2, 3, 5 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/0B.hash
@@ -0,0 +1,1 @@
+sha256:d4428061b6fd69b6b45574603ac71060f9756a5c9a1642df322812e3a7ea858b
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/sum ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/1B.hash
@@ -0,0 +1,1 @@
+sha256:d0a48d41dcc572839faa9499b6c316b8d0d7115efac2ac770f062cd5fd6d0181
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/toDouble 3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/0B.hash
@@ -0,0 +1,1 @@
+sha256:e894ad5c9620a9932e7025f1148823d20abada43ec7f6e50cbd89751a7ba638f
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/toDouble 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/1B.hash
@@ -0,0 +1,1 @@
+sha256:3c213998cb56b97b31168d9dce02d084fb5f5103caef41fb6dc375f593749a7d
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/toInteger 3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/0B.hash
@@ -0,0 +1,1 @@
+sha256:aa773179ef61a5d65c774bcc510806531ce8fa97c550961526153e42959e6f76
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Natural/toInteger 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/1B.hash
@@ -0,0 +1,1 @@
+sha256:5abe149a174ca99b6da92c97b0a7c8900a5f9acddb720bf7173eef877c6a187b
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/all Natural Natural/even (Some 3)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/all Natural Natural/even (None Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/all/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/any Natural Natural/even (Some 2)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/0B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/any Natural Natural/even (None Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/any/1B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/0A.dhall
@@ -0,0 +1,7 @@
+../../../../../../Prelude/Optional/build
+Natural
+( λ(optional : Type)
+→ λ(some : Natural → optional)
+→ λ(none : optional)
+→ some 1
+)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/0B.hash
@@ -0,0 +1,1 @@
+sha256:033652a641ac54a27f52b874877889c6a9c7f8c435dae9f8e956e4d402d6f2c7
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/1A.dhall
@@ -0,0 +1,7 @@
+../../../../../../Prelude/Optional/build
+Natural
+( λ(optional : Type)
+→ λ(some : Natural → optional)
+→ λ(none : optional)
+→ none
+)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/build/1B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/concat Natural (Some (Some 1))
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/0B.hash
@@ -0,0 +1,1 @@
+sha256:033652a641ac54a27f52b874877889c6a9c7f8c435dae9f8e956e4d402d6f2c7
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/concat Natural (Some (None Natural))
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/1B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/2A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/2A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/concat Natural (None (Optional Natural))
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/2B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/2B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/2B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/filter Natural Natural/even (Some 2)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/0B.hash
@@ -0,0 +1,1 @@
+sha256:1400a0fa058e1c8fc58926856000d82d99fad204bf48e36f54dccaafc0acd3a0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/filter Natural Natural/odd (Some 2)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/1B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/fold Natural (Some 2) Natural (λ(x : Natural) → x) 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/0B.hash
@@ -0,0 +1,1 @@
+sha256:4caf97e8c445d4d4b5c5b992973e098ed4ae88a355915f5a59db640a589bc9cb
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/fold Natural (None Natural) Natural (λ(x : Natural) → x) 0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/1B.hash
@@ -0,0 +1,1 @@
+sha256:d0a48d41dcc572839faa9499b6c316b8d0d7115efac2ac770f062cd5fd6d0181
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/head Natural [ None Natural, Some 1, Some 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/0B.hash
@@ -0,0 +1,1 @@
+sha256:033652a641ac54a27f52b874877889c6a9c7f8c435dae9f8e956e4d402d6f2c7
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/head Natural [ None Natural, None Natural ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/1B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/2A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/2A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/head Natural ([] : List (Optional Natural))
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/2B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/2B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/head/2B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/last Natural [ None Natural, Some 1, Some 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/0B.hash
@@ -0,0 +1,1 @@
+sha256:1400a0fa058e1c8fc58926856000d82d99fad204bf48e36f54dccaafc0acd3a0
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/last Natural [ None Natural, None Natural ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/1B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/2A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/2A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/2A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/last Natural ([] : List (Optional Natural))
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/2B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/2B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/last/2B.hash
@@ -0,0 +1,1 @@
+sha256:bfdc295856e05e3610d202d9c12f56f7c423de81cd6069f590786ca176b94df3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/length Natural (Some 2)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/0B.hash
@@ -0,0 +1,1 @@
+sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/length Natural (None Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/length/1B.hash
@@ -0,0 +1,1 @@
+sha256:d0a48d41dcc572839faa9499b6c316b8d0d7115efac2ac770f062cd5fd6d0181
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/map Natural Bool Natural/even (Some 3)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/0B.hash
@@ -0,0 +1,1 @@
+sha256:3af5b2f9dcc58e7950e2f0281f796de2ac5e87554ae758e38c36cd55cf53743e
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/map Natural Bool Natural/even (None Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/map/1B.hash
@@ -0,0 +1,1 @@
+sha256:2c9884f8336ed425b13068919b216c42be286636c657faa15b911382436b3f00
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/null Natural (Some 2)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/0B.hash
@@ -0,0 +1,1 @@
+sha256:2017ff3461395672aa0aa4f64894fd2f95a4b120e2690e8951656d79adc2eed2
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/null Natural (None Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/null/1B.hash
@@ -0,0 +1,1 @@
+sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/toList Natural (Some 1)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/0B.hash
@@ -0,0 +1,1 @@
+sha256:75be41333b1ef7a6f5c5cfbcd6097681f3968736dc393b9bf53b2e609564217f
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/toList Natural (None Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/1B.hash
@@ -0,0 +1,1 @@
+sha256:d79a2e0e14809ab2dbd2d180e60da8e129a5fb197bdd0caed57e3828402e48a9
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/unzip Text Bool (Some { _1 = "ABC", _2 = True })
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/0B.hash
@@ -0,0 +1,1 @@
+sha256:5604f6ef6cf7385166101c61738c34a84983f1b7b4695858fa2068fc669eb2c1
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Optional/unzip Text Bool (None { _1 : Text, _2 : Bool })
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/1B.hash
@@ -0,0 +1,1 @@
+sha256:6de82c54048538c97063c9ae31919b4ca21018ae7119bc123508c136ee653280
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concat [ "ABC", "DEF", "GHI" ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/0B.hash
@@ -0,0 +1,1 @@
+sha256:1f73cf8a6efa724376c3342f9e0104c499e16420732b8c25efc6ab1c18979772
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concat ([] : List Text)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concat/1B.hash
@@ -0,0 +1,1 @@
+sha256:f08ed5225480d827ff3ce74b756afa6330f66d974d6f0d6160d767b5c45642aa
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concatMap Natural (λ(n : Natural) → "${Natural/show n} ") [ 0, 1, 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/0B.hash
@@ -0,0 +1,1 @@
+sha256:fa7f18d0522f120dbf229ff0a55f96792ce76613ba17611c53f3f5efd81df510
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concatMap Natural (λ(n : Natural) → "${Natural/show n} ") ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/1B.hash
@@ -0,0 +1,1 @@
+sha256:f08ed5225480d827ff3ce74b756afa6330f66d974d6f0d6160d767b5c45642aa
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concatMapSep ", " Natural Natural/show [ 0, 1, 2 ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/0B.hash
@@ -0,0 +1,1 @@
+sha256:952e63727a69b5f2db65645167c3a8f60dbc350608562a1c90423c819a1b5baf
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concatMapSep ", " Natural Natural/show ([] : List Natural)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/1B.hash
@@ -0,0 +1,1 @@
+sha256:f08ed5225480d827ff3ce74b756afa6330f66d974d6f0d6160d767b5c45642aa
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concatSep ", " [ "ABC", "DEF", "GHI" ]
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/0B.hash
@@ -0,0 +1,1 @@
+sha256:151ad6d86cced932c9ed6fa5d69b88fb47e160a13762f4d92dcb1e847ccd30d6
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/concatSep ", " ([] : List Text)
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/1B.hash
@@ -0,0 +1,1 @@
+sha256:f08ed5225480d827ff3ce74b756afa6330f66d974d6f0d6160d767b5c45642aa
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/show/0A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/0A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/0A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/show "ABC"
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/show/0B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/0B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/0B.hash
@@ -0,0 +1,1 @@
+sha256:8dffa347e98424c7b1e91e3e3758fc203a4e52fd990dc4b050da18cdb39655d3
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/show/1A.dhall b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/1A.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/1A.dhall
@@ -0,0 +1,1 @@
+../../../../../../Prelude/Text/show "\u0000 \$ \\ \n \u263a"
diff --git a/dhall-lang/tests/semantic-hash/success/prelude/Text/show/1B.hash b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/1B.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/prelude/Text/show/1B.hash
@@ -0,0 +1,1 @@
+sha256:92655b5ee59ddfb1decfd19cf8b37e8f2dd5dbd317f55bcbb780bf6ed2010b0a
diff --git a/dhall-lang/tests/semantic-hash/success/remoteSystemsA.dhall b/dhall-lang/tests/semantic-hash/success/remoteSystemsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/remoteSystemsA.dhall
@@ -0,0 +1,52 @@
+let Text/concatMap = ../../../Prelude/Text/concatMap
+
+let Text/concatSep = ../../../Prelude/Text/concatSep
+
+let Row =
+      { cores :
+          Natural
+      , host :
+          Text
+      , key :
+          Text
+      , mandatoryFeatures :
+          List Text
+      , platforms :
+          List Text
+      , speedFactor :
+          Natural
+      , supportedFeatures :
+          List Text
+      , user :
+          Optional Text
+      }
+
+let renderRow =
+        λ ( row
+          : Row
+          )
+      → let host =
+              Optional/fold
+              Text
+              row.user
+              Text
+              (λ(user : Text) → "${user}@${row.host}")
+              row.host
+        
+        let platforms = Text/concatSep "," row.platforms
+        
+        let key = row.key
+        
+        let cores = Integer/show (Natural/toInteger row.cores)
+        
+        let speedFactor = Integer/show (Natural/toInteger row.speedFactor)
+        
+        let supportedFeatures = Text/concatSep "," row.supportedFeatures
+        
+        let mandatoryFeatures = Text/concatSep "," row.mandatoryFeatures
+        
+        in  ''
+            ${host} ${platforms} ${key} ${cores} ${speedFactor} ${supportedFeatures} ${mandatoryFeatures}
+            ''
+
+in  Text/concatMap Row renderRow
diff --git a/dhall-lang/tests/semantic-hash/success/remoteSystemsB.hash b/dhall-lang/tests/semantic-hash/success/remoteSystemsB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/remoteSystemsB.hash
@@ -0,0 +1,1 @@
+sha256:1591a538ac60355f6f988d01edb3b8622d2631b93177a90ad7172e0f88b67da5
diff --git a/dhall-lang/tests/semantic-hash/success/simple/doubleShowA.dhall b/dhall-lang/tests/semantic-hash/success/simple/doubleShowA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/doubleShowA.dhall
@@ -0,0 +1,6 @@
+{ example0 = Double/show -0.42
+, example1 = Double/show 13.37
+, example2 = Double/show NaN
+, example3 = Double/show Infinity
+, example4 = Double/show -Infinity
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simple/doubleShowB.hash b/dhall-lang/tests/semantic-hash/success/simple/doubleShowB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/doubleShowB.hash
@@ -0,0 +1,1 @@
+sha256:519ea07f25f759795d4326f06a97401fe0a5efe7823139976d1158c939ee6176
diff --git a/dhall-lang/tests/semantic-hash/success/simple/enumA.dhall b/dhall-lang/tests/semantic-hash/success/simple/enumA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/enumA.dhall
@@ -0,0 +1,8 @@
+let Role = < Wizard | Fighter | Rogue >
+
+let show : Role → Text
+         =
+        λ(x : Role)
+      → merge { Wizard = "Wizard", Fighter = "Fighter", Rogue = "Rogue" } x
+
+in  show Role.Wizard
diff --git a/dhall-lang/tests/semantic-hash/success/simple/enumB.hash b/dhall-lang/tests/semantic-hash/success/simple/enumB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/enumB.hash
@@ -0,0 +1,1 @@
+sha256:0e6e479638f87eca04805548d70b5de9d22c5359e7d84ccbcfe148577bc25a4e
diff --git a/dhall-lang/tests/semantic-hash/success/simple/integerShowA.dhall b/dhall-lang/tests/semantic-hash/success/simple/integerShowA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/integerShowA.dhall
@@ -0,0 +1,4 @@
+{ example0 = Integer/show +1337
+, example1 = Integer/show -42
+, example2 = Integer/show +0
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simple/integerShowB.hash b/dhall-lang/tests/semantic-hash/success/simple/integerShowB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/integerShowB.hash
@@ -0,0 +1,1 @@
+sha256:7ff01ad1c68550be713834548385874f38fd7b989e3c4b8cad7125bc266e2bb3
diff --git a/dhall-lang/tests/semantic-hash/success/simple/integerToDoubleA.dhall b/dhall-lang/tests/semantic-hash/success/simple/integerToDoubleA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/integerToDoubleA.dhall
@@ -0,0 +1,10 @@
+[ Integer/toDouble +179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792
+, Integer/toDouble +179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791
+, Integer/toDouble +179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218945
+, Integer/toDouble +179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944
+
+, Integer/toDouble -179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792
+, Integer/toDouble -179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791
+, Integer/toDouble -179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218945
+, Integer/toDouble -179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944
+]
diff --git a/dhall-lang/tests/semantic-hash/success/simple/integerToDoubleB.hash b/dhall-lang/tests/semantic-hash/success/simple/integerToDoubleB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/integerToDoubleB.hash
@@ -0,0 +1,1 @@
+sha256:439d81a4aee03a27da069772c4ea51d64c025faac1a2e749734cc5e4cf0f2484
diff --git a/dhall-lang/tests/semantic-hash/success/simple/letletA.dhall b/dhall-lang/tests/semantic-hash/success/simple/letletA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/letletA.dhall
@@ -0,0 +1,3 @@
+let l = λ(n : Natural) → λ(m : Natural) → λ(x : Natural) → n + m * x
+let f = l 2 3
+in  f 445
diff --git a/dhall-lang/tests/semantic-hash/success/simple/letletB.hash b/dhall-lang/tests/semantic-hash/success/simple/letletB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/letletB.hash
@@ -0,0 +1,1 @@
+sha256:97285916da4ee4a1e108af044b449edcdfb818c2f55aefd64f0af76b347da16d
diff --git a/dhall-lang/tests/semantic-hash/success/simple/listBuildA.dhall b/dhall-lang/tests/semantic-hash/success/simple/listBuildA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/listBuildA.dhall
@@ -0,0 +1,22 @@
+{ example0 =
+    List/build
+    Bool
+    (   λ(list : Type)
+      → λ(cons : Bool → list → list)
+      → λ(nil : list)
+      → cons True (cons False nil)
+    )
+, example1 =
+    List/build
+    Bool
+    (λ(x : Type) → λ(x : Bool → x → x) → λ(x : x@1) → x@1 True (x@1 False x))
+, example2 =
+      λ(id : ∀(a : Type) → a → a)
+    → List/build
+      Bool
+      (   λ(list : Type)
+        → λ(cons : Bool → list → list)
+        → λ(nil : list)
+        → id list (cons True (cons False nil))
+      )
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simple/listBuildB.hash b/dhall-lang/tests/semantic-hash/success/simple/listBuildB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/listBuildB.hash
@@ -0,0 +1,1 @@
+sha256:f8f03d5be6563b275253b4d995499e783112ff98ed231855e9152ba45970f8c7
diff --git a/dhall-lang/tests/semantic-hash/success/simple/multiLineA.dhall b/dhall-lang/tests/semantic-hash/success/simple/multiLineA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/multiLineA.dhall
@@ -0,0 +1,4 @@
+''
+ foo
+bar
+''
diff --git a/dhall-lang/tests/semantic-hash/success/simple/multiLineB.hash b/dhall-lang/tests/semantic-hash/success/simple/multiLineB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/multiLineB.hash
@@ -0,0 +1,1 @@
+sha256:2ab4fb081094ad6729186b7cd8a0ec67f7845bbea59ec5a484bff2de42390137
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalBuildA.dhall b/dhall-lang/tests/semantic-hash/success/simple/naturalBuildA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalBuildA.dhall
@@ -0,0 +1,18 @@
+{ example0 =
+    Natural/build
+    (   λ(natural : Type)
+      → λ(succ : natural → natural)
+      → λ(zero : natural)
+      → succ zero
+    )
+, example1 =
+    Natural/build (λ(x : Type) → λ(x : x → x) → λ(x : x@1) → x@1 x)
+, example2 =
+      λ(id : ∀(a : Type) → a → a)
+    → Natural/build
+      (   λ(natural : Type)
+        → λ(succ : natural → natural)
+        → λ(zero : natural)
+        → id natural (succ zero)
+      )
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalBuildB.hash b/dhall-lang/tests/semantic-hash/success/simple/naturalBuildB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalBuildB.hash
@@ -0,0 +1,1 @@
+sha256:3e14f3cbed2ddcc78cb9bf66bf2372c2059fd6c4b406ca309a05b89b7f62b011
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalPlusA.dhall b/dhall-lang/tests/semantic-hash/success/simple/naturalPlusA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalPlusA.dhall
@@ -0,0 +1,1 @@
+1 + 2
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalPlusB.hash b/dhall-lang/tests/semantic-hash/success/simple/naturalPlusB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalPlusB.hash
@@ -0,0 +1,1 @@
+sha256:15f52ecf91c94c1baac02d5a4964b2ed8fa401641a2c8a95e8306ec7c1e3b8d2
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalShowA.dhall b/dhall-lang/tests/semantic-hash/success/simple/naturalShowA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalShowA.dhall
@@ -0,0 +1,1 @@
+Natural/show 42
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalShowB.hash b/dhall-lang/tests/semantic-hash/success/simple/naturalShowB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalShowB.hash
@@ -0,0 +1,1 @@
+sha256:3d1abfe685ff9abcf5e1a6bcce9b1a8374fc8ac176c22ef3e0188bd29e7d8c6d
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalToIntegerA.dhall b/dhall-lang/tests/semantic-hash/success/simple/naturalToIntegerA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalToIntegerA.dhall
@@ -0,0 +1,1 @@
+Natural/toInteger 1
diff --git a/dhall-lang/tests/semantic-hash/success/simple/naturalToIntegerB.hash b/dhall-lang/tests/semantic-hash/success/simple/naturalToIntegerB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/naturalToIntegerB.hash
@@ -0,0 +1,1 @@
+sha256:d2cbb0e226f332ea76d63b3ae4332780c4051d1388d3e190debd5441f4595a04
diff --git a/dhall-lang/tests/semantic-hash/success/simple/optionalBuildA.dhall b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildA.dhall
@@ -0,0 +1,32 @@
+{ example0 =
+    Optional/build
+    Natural
+    (   λ(optional : Type)
+      → λ(just : Natural → optional)
+      → λ(nothing : optional)
+      → just 1
+    )
+, example1 =
+    Optional/build
+    Integer
+    (λ(optional : Type) → λ(x : Integer → optional) → λ(x : optional) → x@1 +1)
+, example2 =
+      λ(id : ∀(a : Type) → a → a)
+    → Optional/build
+      Bool
+      (   λ(optional : Type)
+        → λ(just : Bool → optional)
+        → λ(nothing : optional)
+        → id optional (just True)
+      )
+, example3 =
+      λ(a : Type)
+    → λ(x : a)
+    → Optional/build
+      a
+      (   λ(optional : Type)
+        → λ(just : a → optional)
+        → λ(nothing : optional)
+        → just x
+      )
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simple/optionalBuildB.hash b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildB.hash
@@ -0,0 +1,1 @@
+sha256:57937e066a166baf3d712e744d9aec91894f14d7b25138a6f60c7b7577cc986b
diff --git a/dhall-lang/tests/semantic-hash/success/simple/optionalBuildFoldA.dhall b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildFoldA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildFoldA.dhall
@@ -0,0 +1,1 @@
+Optional/build Text (Optional/fold Text (Some "foo"))
diff --git a/dhall-lang/tests/semantic-hash/success/simple/optionalBuildFoldB.hash b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildFoldB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/optionalBuildFoldB.hash
@@ -0,0 +1,1 @@
+sha256:29dc3b0c8df03521d3bb92d3b7d996a70a5dbca8de8607ffaf9f888a2213e73b
diff --git a/dhall-lang/tests/semantic-hash/success/simple/optionalFoldA.dhall b/dhall-lang/tests/semantic-hash/success/simple/optionalFoldA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/optionalFoldA.dhall
@@ -0,0 +1,7 @@
+    let f =
+            λ(o : Optional Text)
+          → Optional/fold Text o Natural (λ(j : Text) → 1) 2
+
+in  { example0 = f (Some "foo")
+    , example1 = f (None Text)
+    }
diff --git a/dhall-lang/tests/semantic-hash/success/simple/optionalFoldB.hash b/dhall-lang/tests/semantic-hash/success/simple/optionalFoldB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/optionalFoldB.hash
@@ -0,0 +1,1 @@
+sha256:225b0c8ffa394c837aebfce636806e3f39511d87331b6ef734311689a2ec8a7e
diff --git a/dhall-lang/tests/semantic-hash/success/simple/sortOperatorA.dhall b/dhall-lang/tests/semantic-hash/success/simple/sortOperatorA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/sortOperatorA.dhall
@@ -0,0 +1,1 @@
+{ b = 2 } // { a = 1 }
diff --git a/dhall-lang/tests/semantic-hash/success/simple/sortOperatorB.hash b/dhall-lang/tests/semantic-hash/success/simple/sortOperatorB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simple/sortOperatorB.hash
@@ -0,0 +1,1 @@
+sha256:97090822fa42ff0245484a518e2457f85271f4c36fc9462b82498d85b2cc98fc
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/andA.dhall b/dhall-lang/tests/semantic-hash/success/simplifications/andA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/andA.dhall
@@ -0,0 +1,6 @@
+{ example0 = λ(x : Bool) → x && True
+, example1 = λ(x : Bool) → True && x
+, example2 = λ(x : Bool) → x && False
+, example3 = λ(x : Bool) → False && x
+, example4 = λ(x : Bool) → x && x
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/andB.hash b/dhall-lang/tests/semantic-hash/success/simplifications/andB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/andB.hash
@@ -0,0 +1,1 @@
+sha256:ceac399e820b2a86c65077e07e77bb62f10253fda02a51e438457adf4368b580
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/eqA.dhall b/dhall-lang/tests/semantic-hash/success/simplifications/eqA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/eqA.dhall
@@ -0,0 +1,4 @@
+{ example0 = λ(x : Bool) → x == True
+, example1 = λ(x : Bool) → True == x
+, example2 = λ(x : Bool) → x == x
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/eqB.hash b/dhall-lang/tests/semantic-hash/success/simplifications/eqB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/eqB.hash
@@ -0,0 +1,1 @@
+sha256:5524bd7e3ecdeaa98a25f51f40a1cb33896c2e3e0e21e14c939d17dac946fa4d
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/ifThenElseA.dhall b/dhall-lang/tests/semantic-hash/success/simplifications/ifThenElseA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/ifThenElseA.dhall
@@ -0,0 +1,3 @@
+{ example0 = λ(x : Bool) → if x then True else False
+, example1 = λ(x : Bool) → λ(y : Text) → if x then y else y
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/ifThenElseB.hash b/dhall-lang/tests/semantic-hash/success/simplifications/ifThenElseB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/ifThenElseB.hash
@@ -0,0 +1,1 @@
+sha256:40b3cdc4cc8f2ff9b733e5e8d8613ec8c3d17fc11f22cfd96d8109765c15add5
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/neA.dhall b/dhall-lang/tests/semantic-hash/success/simplifications/neA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/neA.dhall
@@ -0,0 +1,4 @@
+{ example0 = λ(x : Bool) → x != False
+, example1 = λ(x : Bool) → False != x
+, example2 = λ(x : Bool) → x != x
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/neB.hash b/dhall-lang/tests/semantic-hash/success/simplifications/neB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/neB.hash
@@ -0,0 +1,1 @@
+sha256:69d11e754a064f8abe927e5df446896372a6367135e08c52b5c2274e3bd03bc7
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/orA.dhall b/dhall-lang/tests/semantic-hash/success/simplifications/orA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/orA.dhall
@@ -0,0 +1,6 @@
+{ example0 = λ(x : Bool) → x || True
+, example1 = λ(x : Bool) → True || x
+, example2 = λ(x : Bool) → x || False
+, example3 = λ(x : Bool) → False || x
+, example4 = λ(x : Bool) → x || x
+}
diff --git a/dhall-lang/tests/semantic-hash/success/simplifications/orB.hash b/dhall-lang/tests/semantic-hash/success/simplifications/orB.hash
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/semantic-hash/success/simplifications/orB.hash
@@ -0,0 +1,1 @@
+sha256:41437a8eb5753b2d340fc27cc192265459da57cd65d487447a261f2eede2f5ef
diff --git a/dhall-lang/tests/type-inference/success/simple/alternativesAreTypesA.dhall b/dhall-lang/tests/type-inference/success/simple/alternativesAreTypesA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/simple/alternativesAreTypesA.dhall
@@ -0,0 +1,1 @@
+< Left = List | Right : Type >
diff --git a/dhall-lang/tests/type-inference/success/simple/alternativesAreTypesB.dhall b/dhall-lang/tests/type-inference/success/simple/alternativesAreTypesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/simple/alternativesAreTypesB.dhall
@@ -0,0 +1,1 @@
+< Left : Type → Type | Right : Type >
diff --git a/dhall-lang/tests/type-inference/success/simple/kindParameterA.dhall b/dhall-lang/tests/type-inference/success/simple/kindParameterA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/simple/kindParameterA.dhall
@@ -0,0 +1,1 @@
+λ(k : Kind) → λ(a : k → k → Type) → λ(x : k) → a x
diff --git a/dhall-lang/tests/type-inference/success/simple/kindParameterB.dhall b/dhall-lang/tests/type-inference/success/simple/kindParameterB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/simple/kindParameterB.dhall
@@ -0,0 +1,1 @@
+∀(k : Kind) → ∀(a : k → k → Type) → ∀(x : k) → k → Type
diff --git a/dhall-lang/tests/type-inference/success/unit/BoolA.dhall b/dhall-lang/tests/type-inference/success/unit/BoolA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/BoolA.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/BoolB.dhall b/dhall-lang/tests/type-inference/success/unit/BoolB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/BoolB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/DoubleA.dhall b/dhall-lang/tests/type-inference/success/unit/DoubleA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/DoubleA.dhall
@@ -0,0 +1,1 @@
+Double
diff --git a/dhall-lang/tests/type-inference/success/unit/DoubleB.dhall b/dhall-lang/tests/type-inference/success/unit/DoubleB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/DoubleB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/DoubleLiteralA.dhall b/dhall-lang/tests/type-inference/success/unit/DoubleLiteralA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/DoubleLiteralA.dhall
@@ -0,0 +1,1 @@
+1.0
diff --git a/dhall-lang/tests/type-inference/success/unit/DoubleLiteralB.dhall b/dhall-lang/tests/type-inference/success/unit/DoubleLiteralB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/DoubleLiteralB.dhall
@@ -0,0 +1,1 @@
+Double
diff --git a/dhall-lang/tests/type-inference/success/unit/DoubleShowA.dhall b/dhall-lang/tests/type-inference/success/unit/DoubleShowA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/DoubleShowA.dhall
@@ -0,0 +1,1 @@
+Double/show
diff --git a/dhall-lang/tests/type-inference/success/unit/DoubleShowB.dhall b/dhall-lang/tests/type-inference/success/unit/DoubleShowB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/DoubleShowB.dhall
@@ -0,0 +1,1 @@
+Double → Text
diff --git a/dhall-lang/tests/type-inference/success/unit/FalseA.dhall b/dhall-lang/tests/type-inference/success/unit/FalseA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FalseA.dhall
@@ -0,0 +1,1 @@
+False
diff --git a/dhall-lang/tests/type-inference/success/unit/FalseB.dhall b/dhall-lang/tests/type-inference/success/unit/FalseB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FalseB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionA.dhall
@@ -0,0 +1,1 @@
+λ(_ : Bool) → _
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionApplicationA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionApplicationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionApplicationA.dhall
@@ -0,0 +1,1 @@
+(λ(_ : Bool) → _) True
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionApplicationB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionApplicationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionApplicationB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionB.dhall
@@ -0,0 +1,1 @@
+Bool → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionNamedArgA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionNamedArgA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionNamedArgA.dhall
@@ -0,0 +1,1 @@
+λ(x : Bool) → x
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionNamedArgB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionNamedArgB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionNamedArgB.dhall
@@ -0,0 +1,1 @@
+∀(x : Bool) → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindKindA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindKindA.dhall
@@ -0,0 +1,1 @@
+Kind → Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindKindB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindKindB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTermA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTermA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTermA.dhall
@@ -0,0 +1,1 @@
+Kind → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTermB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTermB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTermB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTypeA.dhall
@@ -0,0 +1,1 @@
+Kind → Type
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeKindTypeB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeTermTermA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTermTermA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTermTermA.dhall
@@ -0,0 +1,1 @@
+Bool → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeTermTermB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTermTermB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTermTermB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTermA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTermA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTermA.dhall
@@ -0,0 +1,1 @@
+Type → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTermB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTermB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTermB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTypeA.dhall
@@ -0,0 +1,1 @@
+Type → Type
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeTypeTypeB.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeUsingArgumentA.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeUsingArgumentA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeUsingArgumentA.dhall
@@ -0,0 +1,1 @@
+Type → _
diff --git a/dhall-lang/tests/type-inference/success/unit/FunctionTypeUsingArgumentB.dhall b/dhall-lang/tests/type-inference/success/unit/FunctionTypeUsingArgumentB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/FunctionTypeUsingArgumentB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/IfA.dhall b/dhall-lang/tests/type-inference/success/unit/IfA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IfA.dhall
@@ -0,0 +1,1 @@
+if True then False else True
diff --git a/dhall-lang/tests/type-inference/success/unit/IfB.dhall b/dhall-lang/tests/type-inference/success/unit/IfB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IfB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/IfNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/IfNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IfNormalizeArgumentsA.dhall
@@ -0,0 +1,9 @@
+      if if True then True else True
+
+then  if True then False else True
+
+else  if True
+
+then  True
+
+else  True
diff --git a/dhall-lang/tests/type-inference/success/unit/IfNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/IfNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IfNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerA.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerA.dhall
@@ -0,0 +1,1 @@
+Integer
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerB.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerLiteralA.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerLiteralA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerLiteralA.dhall
@@ -0,0 +1,1 @@
++1
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerLiteralB.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerLiteralB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerLiteralB.dhall
@@ -0,0 +1,1 @@
+Integer
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerShowA.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerShowA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerShowA.dhall
@@ -0,0 +1,1 @@
+Integer/show
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerShowB.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerShowB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerShowB.dhall
@@ -0,0 +1,1 @@
+Integer → Text
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerToDoubleA.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerToDoubleA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerToDoubleA.dhall
@@ -0,0 +1,1 @@
+Integer/toDouble
diff --git a/dhall-lang/tests/type-inference/success/unit/IntegerToDoubleB.dhall b/dhall-lang/tests/type-inference/success/unit/IntegerToDoubleB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/IntegerToDoubleB.dhall
@@ -0,0 +1,1 @@
+Integer → Double
diff --git a/dhall-lang/tests/type-inference/success/unit/KindA.dhall b/dhall-lang/tests/type-inference/success/unit/KindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/KindA.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/KindB.dhall b/dhall-lang/tests/type-inference/success/unit/KindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/KindB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/LetA.dhall b/dhall-lang/tests/type-inference/success/unit/LetA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetA.dhall
@@ -0,0 +1,1 @@
+let x = True in x
diff --git a/dhall-lang/tests/type-inference/success/unit/LetB.dhall b/dhall-lang/tests/type-inference/success/unit/LetB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/LetNestedTypeSynonymA.dhall b/dhall-lang/tests/type-inference/success/unit/LetNestedTypeSynonymA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetNestedTypeSynonymA.dhall
@@ -0,0 +1,1 @@
+let x = Bool let y : x = True in y
diff --git a/dhall-lang/tests/type-inference/success/unit/LetNestedTypeSynonymB.dhall b/dhall-lang/tests/type-inference/success/unit/LetNestedTypeSynonymB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetNestedTypeSynonymB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/LetTypeSynonymA.dhall b/dhall-lang/tests/type-inference/success/unit/LetTypeSynonymA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetTypeSynonymA.dhall
@@ -0,0 +1,1 @@
+let x = Bool in True : x
diff --git a/dhall-lang/tests/type-inference/success/unit/LetTypeSynonymB.dhall b/dhall-lang/tests/type-inference/success/unit/LetTypeSynonymB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetTypeSynonymB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/LetWithAnnotationA.dhall b/dhall-lang/tests/type-inference/success/unit/LetWithAnnotationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetWithAnnotationA.dhall
@@ -0,0 +1,1 @@
+let x : Bool = True in x
diff --git a/dhall-lang/tests/type-inference/success/unit/LetWithAnnotationB.dhall b/dhall-lang/tests/type-inference/success/unit/LetWithAnnotationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/LetWithAnnotationB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/ListA.dhall b/dhall-lang/tests/type-inference/success/unit/ListA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListA.dhall
@@ -0,0 +1,1 @@
+List
diff --git a/dhall-lang/tests/type-inference/success/unit/ListB.dhall b/dhall-lang/tests/type-inference/success/unit/ListB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListB.dhall
@@ -0,0 +1,1 @@
+Type → Type
diff --git a/dhall-lang/tests/type-inference/success/unit/ListBuildA.dhall b/dhall-lang/tests/type-inference/success/unit/ListBuildA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListBuildA.dhall
@@ -0,0 +1,1 @@
+List/build
diff --git a/dhall-lang/tests/type-inference/success/unit/ListBuildB.dhall b/dhall-lang/tests/type-inference/success/unit/ListBuildB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListBuildB.dhall
@@ -0,0 +1,3 @@
+  ∀(a : Type)
+→ (∀(list : Type) → ∀(cons : a → list → list) → ∀(nil : list) → list)
+→ List a
diff --git a/dhall-lang/tests/type-inference/success/unit/ListFoldA.dhall b/dhall-lang/tests/type-inference/success/unit/ListFoldA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListFoldA.dhall
@@ -0,0 +1,1 @@
+List/fold
diff --git a/dhall-lang/tests/type-inference/success/unit/ListFoldB.dhall b/dhall-lang/tests/type-inference/success/unit/ListFoldB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListFoldB.dhall
@@ -0,0 +1,6 @@
+  ∀(a : Type)
+→ List a
+→ ∀(list : Type)
+→ ∀(cons : a → list → list)
+→ ∀(nil : list)
+→ list
diff --git a/dhall-lang/tests/type-inference/success/unit/ListHeadA.dhall b/dhall-lang/tests/type-inference/success/unit/ListHeadA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListHeadA.dhall
@@ -0,0 +1,1 @@
+List/head
diff --git a/dhall-lang/tests/type-inference/success/unit/ListHeadB.dhall b/dhall-lang/tests/type-inference/success/unit/ListHeadB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListHeadB.dhall
@@ -0,0 +1,1 @@
+∀(a : Type) → List a → Optional a
diff --git a/dhall-lang/tests/type-inference/success/unit/ListIndexedA.dhall b/dhall-lang/tests/type-inference/success/unit/ListIndexedA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListIndexedA.dhall
@@ -0,0 +1,1 @@
+List/indexed
diff --git a/dhall-lang/tests/type-inference/success/unit/ListIndexedB.dhall b/dhall-lang/tests/type-inference/success/unit/ListIndexedB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListIndexedB.dhall
@@ -0,0 +1,1 @@
+∀(a : Type) → List a → List { index : Natural, value : a }
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLastA.dhall b/dhall-lang/tests/type-inference/success/unit/ListLastA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLastA.dhall
@@ -0,0 +1,1 @@
+List/last
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLastB.dhall b/dhall-lang/tests/type-inference/success/unit/ListLastB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLastB.dhall
@@ -0,0 +1,1 @@
+∀(a : Type) → List a → Optional a
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLengthA.dhall b/dhall-lang/tests/type-inference/success/unit/ListLengthA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLengthA.dhall
@@ -0,0 +1,1 @@
+List/length
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLengthB.dhall b/dhall-lang/tests/type-inference/success/unit/ListLengthB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLengthB.dhall
@@ -0,0 +1,1 @@
+∀(a : Type) → List a → Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyA.dhall
@@ -0,0 +1,1 @@
+[] : List Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyB.dhall
@@ -0,0 +1,1 @@
+List Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyNormalizeAnnotationA.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyNormalizeAnnotationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyNormalizeAnnotationA.dhall
@@ -0,0 +1,1 @@
+[] : { x = List Bool }.x
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyNormalizeAnnotationB.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyNormalizeAnnotationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralEmptyNormalizeAnnotationB.dhall
@@ -0,0 +1,1 @@
+List Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+[ if True then False else True ]
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+List Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralOneA.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralOneA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralOneA.dhall
@@ -0,0 +1,1 @@
+[ True ]
diff --git a/dhall-lang/tests/type-inference/success/unit/ListLiteralOneB.dhall b/dhall-lang/tests/type-inference/success/unit/ListLiteralOneB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListLiteralOneB.dhall
@@ -0,0 +1,1 @@
+List Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/ListReverseA.dhall b/dhall-lang/tests/type-inference/success/unit/ListReverseA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListReverseA.dhall
@@ -0,0 +1,1 @@
+List/reverse
diff --git a/dhall-lang/tests/type-inference/success/unit/ListReverseB.dhall b/dhall-lang/tests/type-inference/success/unit/ListReverseB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ListReverseB.dhall
@@ -0,0 +1,1 @@
+∀(a : Type) → List a → List a
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeEmptyUnionA.dhall b/dhall-lang/tests/type-inference/success/unit/MergeEmptyUnionA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeEmptyUnionA.dhall
@@ -0,0 +1,1 @@
+λ(x : <>) → merge {=} x : Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeEmptyUnionB.dhall b/dhall-lang/tests/type-inference/success/unit/MergeEmptyUnionB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeEmptyUnionB.dhall
@@ -0,0 +1,1 @@
+∀(x : <>) → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeOneA.dhall b/dhall-lang/tests/type-inference/success/unit/MergeOneA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeOneA.dhall
@@ -0,0 +1,1 @@
+merge { x = λ(_ : Bool) → _ } (< x : Bool >.x True)
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeOneB.dhall b/dhall-lang/tests/type-inference/success/unit/MergeOneB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeOneB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeOneEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/MergeOneEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeOneEmptyA.dhall
@@ -0,0 +1,1 @@
+merge { x = 5 } < x >.x
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeOneEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/MergeOneEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeOneEmptyB.dhall
@@ -0,0 +1,1 @@
+Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationA.dhall b/dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationA.dhall
@@ -0,0 +1,1 @@
+merge { x = λ(_ : Bool) → _ } (< x : Bool >.x True) : Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationB.dhall b/dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalA.dhall
@@ -0,0 +1,1 @@
+Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalBuildA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalBuildA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalBuildA.dhall
@@ -0,0 +1,1 @@
+Natural/build
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalBuildB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalBuildB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalBuildB.dhall
@@ -0,0 +1,6 @@
+  (   ∀(natural : Type)
+    → ∀(succ : natural → natural)
+    → ∀(zero : natural)
+    → natural
+  )
+→ Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalEvenA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalEvenA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalEvenA.dhall
@@ -0,0 +1,1 @@
+Natural/even
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalEvenB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalEvenB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalEvenB.dhall
@@ -0,0 +1,1 @@
+Natural → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalFoldA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalFoldA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalFoldA.dhall
@@ -0,0 +1,1 @@
+Natural/fold
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalFoldB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalFoldB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalFoldB.dhall
@@ -0,0 +1,5 @@
+  Natural
+→ ∀(natural : Type)
+→ ∀(succ : natural → natural)
+→ ∀(zero : natural)
+→ natural
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalIsZeroA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalIsZeroA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalIsZeroA.dhall
@@ -0,0 +1,1 @@
+Natural/isZero
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalIsZeroB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalIsZeroB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalIsZeroB.dhall
@@ -0,0 +1,1 @@
+Natural → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalLiteralA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalLiteralA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalLiteralA.dhall
@@ -0,0 +1,1 @@
+1
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalLiteralB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalLiteralB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalLiteralB.dhall
@@ -0,0 +1,1 @@
+Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalOddA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalOddA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalOddA.dhall
@@ -0,0 +1,1 @@
+Natural/odd
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalOddB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalOddB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalOddB.dhall
@@ -0,0 +1,1 @@
+Natural → Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalShowA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalShowA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalShowA.dhall
@@ -0,0 +1,1 @@
+Natural/show
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalShowB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalShowB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalShowB.dhall
@@ -0,0 +1,1 @@
+Natural → Text
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalToIntegerA.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalToIntegerA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalToIntegerA.dhall
@@ -0,0 +1,1 @@
+Natural/toInteger
diff --git a/dhall-lang/tests/type-inference/success/unit/NaturalToIntegerB.dhall b/dhall-lang/tests/type-inference/success/unit/NaturalToIntegerB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NaturalToIntegerB.dhall
@@ -0,0 +1,1 @@
+Natural → Integer
diff --git a/dhall-lang/tests/type-inference/success/unit/NoneA.dhall b/dhall-lang/tests/type-inference/success/unit/NoneA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NoneA.dhall
@@ -0,0 +1,1 @@
+None
diff --git a/dhall-lang/tests/type-inference/success/unit/NoneB.dhall b/dhall-lang/tests/type-inference/success/unit/NoneB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/NoneB.dhall
@@ -0,0 +1,1 @@
+∀(A : Type) → Optional A
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorAndA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorAndA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorAndA.dhall
@@ -0,0 +1,1 @@
+True && False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorAndB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorAndB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorAndB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorAndNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorAndNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorAndNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+True && False && False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorAndNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorAndNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorAndNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorEqualA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorEqualA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorEqualA.dhall
@@ -0,0 +1,1 @@
+True == False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorEqualB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorEqualB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorEqualB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorEqualNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorEqualNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorEqualNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+True == False == False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorEqualNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorEqualNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorEqualNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateA.dhall
@@ -0,0 +1,1 @@
+[ True ] # [ True ]
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateB.dhall
@@ -0,0 +1,1 @@
+List Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+(if True then [ True ] else [ False ]) # (if True then [ True ] else [ False ])
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorListConcatenateNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+List Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualA.dhall
@@ -0,0 +1,1 @@
+True != False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+True != False != False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorNotEqualNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorOrA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorOrA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorOrA.dhall
@@ -0,0 +1,1 @@
+True || False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorOrB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorOrB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorOrB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorOrNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorOrNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorOrNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+True || False || False
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorOrNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorOrNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorOrNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorPlusA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorPlusA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorPlusA.dhall
@@ -0,0 +1,1 @@
+1 + 1
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorPlusB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorPlusB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorPlusB.dhall
@@ -0,0 +1,1 @@
+Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorPlusNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorPlusNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorPlusNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+1 + 1 + (1 + 1)
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorPlusNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorPlusNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorPlusNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateA.dhall
@@ -0,0 +1,1 @@
+"a" ++ "b"
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateB.dhall
@@ -0,0 +1,1 @@
+Text
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+"x" ++ "a" ++ ("y" ++ "b")
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTextConcatenateNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Text
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTimesA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTimesA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTimesA.dhall
@@ -0,0 +1,1 @@
+1 * 1
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTimesB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTimesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTimesB.dhall
@@ -0,0 +1,1 @@
+Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTimesNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTimesNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTimesNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+1 * 1 * (1 * 1)
diff --git a/dhall-lang/tests/type-inference/success/unit/OperatorTimesNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/OperatorTimesNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OperatorTimesNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Natural
diff --git a/dhall-lang/tests/type-inference/success/unit/OptionalA.dhall b/dhall-lang/tests/type-inference/success/unit/OptionalA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OptionalA.dhall
@@ -0,0 +1,1 @@
+Optional
diff --git a/dhall-lang/tests/type-inference/success/unit/OptionalB.dhall b/dhall-lang/tests/type-inference/success/unit/OptionalB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OptionalB.dhall
@@ -0,0 +1,1 @@
+Type → Type
diff --git a/dhall-lang/tests/type-inference/success/unit/OptionalBuildA.dhall b/dhall-lang/tests/type-inference/success/unit/OptionalBuildA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OptionalBuildA.dhall
@@ -0,0 +1,1 @@
+Optional/build
diff --git a/dhall-lang/tests/type-inference/success/unit/OptionalBuildB.dhall b/dhall-lang/tests/type-inference/success/unit/OptionalBuildB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OptionalBuildB.dhall
@@ -0,0 +1,7 @@
+  ∀(a : Type)
+→ (   ∀(optional : Type)
+    → ∀(just : a → optional)
+    → ∀(nothing : optional)
+    → optional
+  )
+→ Optional a
diff --git a/dhall-lang/tests/type-inference/success/unit/OptionalFoldA.dhall b/dhall-lang/tests/type-inference/success/unit/OptionalFoldA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OptionalFoldA.dhall
@@ -0,0 +1,1 @@
+Optional/fold
diff --git a/dhall-lang/tests/type-inference/success/unit/OptionalFoldB.dhall b/dhall-lang/tests/type-inference/success/unit/OptionalFoldB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/OptionalFoldB.dhall
@@ -0,0 +1,6 @@
+  ∀(a : Type)
+→ Optional a
+→ ∀(optional : Type)
+→ ∀(just : a → optional)
+→ ∀(nothing : optional)
+→ optional
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordEmptyA.dhall
@@ -0,0 +1,1 @@
+{=}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordEmptyB.dhall
@@ -0,0 +1,1 @@
+{}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedKindA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindA.dhall
@@ -0,0 +1,1 @@
+{ a = { b = Type } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedKindB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindB.dhall
@@ -0,0 +1,1 @@
+{ a : { b : Kind } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedKindLikeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindLikeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindLikeA.dhall
@@ -0,0 +1,1 @@
+{ a = { b = Type → Type } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedKindLikeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindLikeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedKindLikeB.dhall
@@ -0,0 +1,1 @@
+{ a : { b : Kind } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeA.dhall
@@ -0,0 +1,1 @@
+{ a = { b = Bool } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeB.dhall
@@ -0,0 +1,1 @@
+{ a : { b : Type } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeLikeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeLikeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeLikeA.dhall
@@ -0,0 +1,1 @@
+{ a = { b = Bool → Bool } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeLikeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeLikeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordNestedTypeLikeB.dhall
@@ -0,0 +1,1 @@
+{ a : { b : Type } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordOneKindA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordOneKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordOneKindA.dhall
@@ -0,0 +1,1 @@
+{ x = Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordOneKindB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordOneKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordOneKindB.dhall
@@ -0,0 +1,1 @@
+{ x : Kind }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordOneTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordOneTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordOneTypeA.dhall
@@ -0,0 +1,1 @@
+{ x = {} }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordOneTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordOneTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordOneTypeB.dhall
@@ -0,0 +1,1 @@
+{ x : Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordOneValueA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordOneValueA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordOneValueA.dhall
@@ -0,0 +1,1 @@
+{ x = {=} }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordOneValueB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordOneValueB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordOneValueB.dhall
@@ -0,0 +1,1 @@
+{ x : {} }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeA.dhall
@@ -0,0 +1,1 @@
+let e = { a = 10, b = "Text" } let s = { a : Natural } in e.(s)
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeB.dhall
@@ -0,0 +1,1 @@
+{ a : Natural }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeEmptyA.dhall
@@ -0,0 +1,1 @@
+let e = { a = 10, b = "Text" } let s = { } in e.(s)
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeEmptyB.dhall
@@ -0,0 +1,1 @@
+{}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeJudgmentalEqualityA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeJudgmentalEqualityA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeJudgmentalEqualityA.dhall
@@ -0,0 +1,5 @@
+-- This tests that we prefer the type from the selector over the inferred field
+-- type from the record
+-- ie the result should be `{ a : Natural → Natural }`
+--                     not `{ a : ∀(x : Natural) → Natural }`
+let e = { a = λ(x : Natural) → x } let s = { a : Natural → Natural } in e.(s)
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeJudgmentalEqualityB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeJudgmentalEqualityB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionByTypeJudgmentalEqualityB.dhall
@@ -0,0 +1,1 @@
+{ a : Natural → Natural }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionEmptyA.dhall
@@ -0,0 +1,1 @@
+{ x = {=} }.{}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionEmptyB.dhall
@@ -0,0 +1,1 @@
+{}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionKindA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionKindA.dhall
@@ -0,0 +1,1 @@
+{ x = Type, y = Type }.{ x }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionKindB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionKindB.dhall
@@ -0,0 +1,1 @@
+{ x : Kind }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionTypeA.dhall
@@ -0,0 +1,1 @@
+{ x = Bool, y = Natural }.{ x }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionTypeB.dhall
@@ -0,0 +1,1 @@
+{ x : Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionValueA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionValueA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionValueA.dhall
@@ -0,0 +1,1 @@
+{ x = {=}, y = {=} }.{ x }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordProjectionValueB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordProjectionValueB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordProjectionValueB.dhall
@@ -0,0 +1,1 @@
+{ x : {} }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordSelectionKindA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordSelectionKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordSelectionKindA.dhall
@@ -0,0 +1,1 @@
+{ x = Type }.x
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordSelectionKindB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordSelectionKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordSelectionKindB.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordSelectionTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordSelectionTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordSelectionTypeA.dhall
@@ -0,0 +1,1 @@
+{ x = {} }.x
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordSelectionTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordSelectionTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordSelectionTypeB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordSelectionValueA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordSelectionValueA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordSelectionValueA.dhall
@@ -0,0 +1,1 @@
+{ x = {=} }.x
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordSelectionValueB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordSelectionValueB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordSelectionValueB.dhall
@@ -0,0 +1,1 @@
+{}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeA.dhall
@@ -0,0 +1,1 @@
+{ x : Bool }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeEmptyA.dhall
@@ -0,0 +1,1 @@
+{}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeEmptyB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeKindA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindA.dhall
@@ -0,0 +1,1 @@
+{ x : Kind }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeKindB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeKindLikeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindLikeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindLikeA.dhall
@@ -0,0 +1,1 @@
+{ a : Kind → Kind }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeKindLikeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindLikeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeKindLikeB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindA.dhall
@@ -0,0 +1,1 @@
+{ a : { b : Kind } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindLikeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindLikeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindLikeA.dhall
@@ -0,0 +1,1 @@
+{ a : { b : Kind → Kind } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindLikeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindLikeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeNestedKindLikeB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeTypeA.dhall
@@ -0,0 +1,1 @@
+{ x : Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecordTypeTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/RecordTypeTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecordTypeTypeB.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeLhsEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeLhsEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeLhsEmptyA.dhall
@@ -0,0 +1,1 @@
+{=} ∧ { x = True }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeLhsEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeLhsEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeLhsEmptyB.dhall
@@ -0,0 +1,1 @@
+{ x : Bool }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyA.dhall
@@ -0,0 +1,1 @@
+{ x = { a = True } } ∧ { x = { b = True } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyB.dhall
@@ -0,0 +1,1 @@
+{ x : { a : Bool, b : Bool } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyKindsA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyKindsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyKindsA.dhall
@@ -0,0 +1,1 @@
+{ x = { a = Type } } ∧ { x = { b = Type } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyKindsB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyKindsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyKindsB.dhall
@@ -0,0 +1,1 @@
+{ x : { a : Kind, b : Kind } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyTypesA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyTypesA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyTypesA.dhall
@@ -0,0 +1,1 @@
+{ x = { a = Bool } } ∧ { x = { b = Natural } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyTypesB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyTypesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRecursivelyTypesB.dhall
@@ -0,0 +1,1 @@
+{ x : { a : Type, b : Type } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRhsEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRhsEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRhsEmptyA.dhall
@@ -0,0 +1,1 @@
+{ x = True } ∧ {=}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRhsEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRhsEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeRhsEmptyB.dhall
@@ -0,0 +1,1 @@
+{ x : Bool }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoA.dhall
@@ -0,0 +1,1 @@
+{ x = True } ∧ { y = True }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoB.dhall
@@ -0,0 +1,1 @@
+{ x : Bool, y : Bool }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoKindsA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoKindsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoKindsA.dhall
@@ -0,0 +1,1 @@
+{ x = Type } ∧ { y = Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoKindsB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoKindsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoKindsB.dhall
@@ -0,0 +1,1 @@
+{ x : Kind, y : Kind }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoTypesA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoTypesA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoTypesA.dhall
@@ -0,0 +1,1 @@
+{ x = Bool } ∧ { y = Natural }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoTypesB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoTypesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordMergeTwoTypesB.dhall
@@ -0,0 +1,1 @@
+{ x : Type, y : Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyA.dhall
@@ -0,0 +1,1 @@
+{ x : { a : Bool } } ⩓ { y : { b : Bool } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyKindsA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyKindsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyKindsA.dhall
@@ -0,0 +1,1 @@
+{ x : { a : Kind } } ⩓ { y : { b : Kind } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyKindsB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyKindsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyKindsB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyTypesA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyTypesA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyTypesA.dhall
@@ -0,0 +1,1 @@
+{ x : { a : Type } } ⩓ { y : { b : Type } }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyTypesB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyTypesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRecursivelyTypesB.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRhsEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRhsEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRhsEmptyA.dhall
@@ -0,0 +1,1 @@
+{ x : Bool } ⩓ {}
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRhsEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRhsEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeRhsEmptyB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoA.dhall
@@ -0,0 +1,1 @@
+{ x : Bool } ⩓ { y : Bool }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoKindsA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoKindsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoKindsA.dhall
@@ -0,0 +1,1 @@
+{ x : Kind } ⩓ { y : Kind }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoKindsB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoKindsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoKindsB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoTypesA.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoTypesA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoTypesA.dhall
@@ -0,0 +1,1 @@
+{ x : Type } ⩓ { y : Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoTypesB.dhall b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoTypesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeTwoTypesB.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeRhsEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeRhsEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeRhsEmptyA.dhall
@@ -0,0 +1,1 @@
+{ x = {=} } ⫽ {=}
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeRhsEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeRhsEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeRhsEmptyB.dhall
@@ -0,0 +1,1 @@
+{ x : {} }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoA.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoA.dhall
@@ -0,0 +1,1 @@
+{ x = {=} } ⫽ { x = {=} }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoB.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoB.dhall
@@ -0,0 +1,1 @@
+{ x : {} }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoDifferentA.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoDifferentA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoDifferentA.dhall
@@ -0,0 +1,1 @@
+{ x = {=} } ⫽ { x = True }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoDifferentB.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoDifferentB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoDifferentB.dhall
@@ -0,0 +1,1 @@
+{ x : Bool }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoKindsA.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoKindsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoKindsA.dhall
@@ -0,0 +1,1 @@
+{ x = Type } ⫽ { x = Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoKindsB.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoKindsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoKindsB.dhall
@@ -0,0 +1,1 @@
+{ x : Kind }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoTypesA.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoTypesA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoTypesA.dhall
@@ -0,0 +1,1 @@
+{ x = Bool } ⫽ { x = Natural }
diff --git a/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoTypesB.dhall b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoTypesB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/RightBiasedRecordMergeTwoTypesB.dhall
@@ -0,0 +1,1 @@
+{ x : Type }
diff --git a/dhall-lang/tests/type-inference/success/unit/SomeTrueA.dhall b/dhall-lang/tests/type-inference/success/unit/SomeTrueA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/SomeTrueA.dhall
@@ -0,0 +1,1 @@
+Some True
diff --git a/dhall-lang/tests/type-inference/success/unit/SomeTrueB.dhall b/dhall-lang/tests/type-inference/success/unit/SomeTrueB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/SomeTrueB.dhall
@@ -0,0 +1,1 @@
+Optional Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/TextA.dhall b/dhall-lang/tests/type-inference/success/unit/TextA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextA.dhall
@@ -0,0 +1,1 @@
+Text
diff --git a/dhall-lang/tests/type-inference/success/unit/TextB.dhall b/dhall-lang/tests/type-inference/success/unit/TextB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/TextLiteralA.dhall b/dhall-lang/tests/type-inference/success/unit/TextLiteralA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextLiteralA.dhall
@@ -0,0 +1,1 @@
+"a"
diff --git a/dhall-lang/tests/type-inference/success/unit/TextLiteralB.dhall b/dhall-lang/tests/type-inference/success/unit/TextLiteralB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextLiteralB.dhall
@@ -0,0 +1,1 @@
+Text
diff --git a/dhall-lang/tests/type-inference/success/unit/TextLiteralNormalizeArgumentsA.dhall b/dhall-lang/tests/type-inference/success/unit/TextLiteralNormalizeArgumentsA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextLiteralNormalizeArgumentsA.dhall
@@ -0,0 +1,1 @@
+"a${if True then "a" else "b"}"
diff --git a/dhall-lang/tests/type-inference/success/unit/TextLiteralNormalizeArgumentsB.dhall b/dhall-lang/tests/type-inference/success/unit/TextLiteralNormalizeArgumentsB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextLiteralNormalizeArgumentsB.dhall
@@ -0,0 +1,1 @@
+Text
diff --git a/dhall-lang/tests/type-inference/success/unit/TextLiteralWithInterpolationA.dhall b/dhall-lang/tests/type-inference/success/unit/TextLiteralWithInterpolationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextLiteralWithInterpolationA.dhall
@@ -0,0 +1,1 @@
+"a${"b"}"
diff --git a/dhall-lang/tests/type-inference/success/unit/TextLiteralWithInterpolationB.dhall b/dhall-lang/tests/type-inference/success/unit/TextLiteralWithInterpolationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextLiteralWithInterpolationB.dhall
@@ -0,0 +1,1 @@
+Text
diff --git a/dhall-lang/tests/type-inference/success/unit/TextShowA.dhall b/dhall-lang/tests/type-inference/success/unit/TextShowA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextShowA.dhall
@@ -0,0 +1,1 @@
+Text/show
diff --git a/dhall-lang/tests/type-inference/success/unit/TextShowB.dhall b/dhall-lang/tests/type-inference/success/unit/TextShowB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TextShowB.dhall
@@ -0,0 +1,1 @@
+Text → Text
diff --git a/dhall-lang/tests/type-inference/success/unit/ToMapA.dhall b/dhall-lang/tests/type-inference/success/unit/ToMapA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ToMapA.dhall
@@ -0,0 +1,1 @@
+toMap { foo= 1, bar= 4, baz= 9 }
diff --git a/dhall-lang/tests/type-inference/success/unit/ToMapB.dhall b/dhall-lang/tests/type-inference/success/unit/ToMapB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/ToMapB.dhall
@@ -0,0 +1,1 @@
+List { mapKey : Text, mapValue : Natural }
diff --git a/dhall-lang/tests/type-inference/success/unit/TrueA.dhall b/dhall-lang/tests/type-inference/success/unit/TrueA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TrueA.dhall
@@ -0,0 +1,1 @@
+True
diff --git a/dhall-lang/tests/type-inference/success/unit/TrueB.dhall b/dhall-lang/tests/type-inference/success/unit/TrueB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TrueB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/TypeA.dhall b/dhall-lang/tests/type-inference/success/unit/TypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TypeA.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/TypeAnnotationA.dhall b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationA.dhall
@@ -0,0 +1,1 @@
+True : Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/TypeAnnotationB.dhall b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationB.dhall
@@ -0,0 +1,1 @@
+Bool
diff --git a/dhall-lang/tests/type-inference/success/unit/TypeAnnotationSortA.dhall b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationSortA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationSortA.dhall
@@ -0,0 +1,1 @@
+Kind : Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/TypeAnnotationSortB.dhall b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationSortB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TypeAnnotationSortB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/TypeB.dhall b/dhall-lang/tests/type-inference/success/unit/TypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/TypeB.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionConstructorEmptyFieldA.dhall b/dhall-lang/tests/type-inference/success/unit/UnionConstructorEmptyFieldA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionConstructorEmptyFieldA.dhall
@@ -0,0 +1,1 @@
+< x | y : Bool >.x
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionConstructorEmptyFieldB.dhall b/dhall-lang/tests/type-inference/success/unit/UnionConstructorEmptyFieldB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionConstructorEmptyFieldB.dhall
@@ -0,0 +1,1 @@
+< x | y : Bool >
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionConstructorFieldA.dhall b/dhall-lang/tests/type-inference/success/unit/UnionConstructorFieldA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionConstructorFieldA.dhall
@@ -0,0 +1,1 @@
+< x : Bool >.x
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionConstructorFieldB.dhall b/dhall-lang/tests/type-inference/success/unit/UnionConstructorFieldB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionConstructorFieldB.dhall
@@ -0,0 +1,1 @@
+∀(x : Bool) → < x : Bool >
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionLiteralOneA.dhall b/dhall-lang/tests/type-inference/success/unit/UnionLiteralOneA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionLiteralOneA.dhall
@@ -0,0 +1,1 @@
+< x = True >
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionLiteralOneB.dhall b/dhall-lang/tests/type-inference/success/unit/UnionLiteralOneB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionLiteralOneB.dhall
@@ -0,0 +1,1 @@
+< x : Bool >
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeEmptyA.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeEmptyA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeEmptyA.dhall
@@ -0,0 +1,1 @@
+<>
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeEmptyB.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeEmptyB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeEmptyB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeKindA.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeKindA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeKindA.dhall
@@ -0,0 +1,1 @@
+< x : Kind >
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeKindB.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeKindB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeKindB.dhall
@@ -0,0 +1,1 @@
+Sort
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeOneA.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeOneA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeOneA.dhall
@@ -0,0 +1,1 @@
+< x : Bool >
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeOneB.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeOneB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeOneB.dhall
@@ -0,0 +1,1 @@
+Type
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeTypeA.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeTypeA.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeTypeA.dhall
@@ -0,0 +1,1 @@
+< x : Type >
diff --git a/dhall-lang/tests/type-inference/success/unit/UnionTypeTypeB.dhall b/dhall-lang/tests/type-inference/success/unit/UnionTypeTypeB.dhall
new file mode 100644
--- /dev/null
+++ b/dhall-lang/tests/type-inference/success/unit/UnionTypeTypeB.dhall
@@ -0,0 +1,1 @@
+Kind
diff --git a/dhall-lang/tests/typecheck/failure/customHeadersUsingBoundVariable.dhall b/dhall-lang/tests/typecheck/failure/customHeadersUsingBoundVariable.dhall
--- a/dhall-lang/tests/typecheck/failure/customHeadersUsingBoundVariable.dhall
+++ b/dhall-lang/tests/typecheck/failure/customHeadersUsingBoundVariable.dhall
@@ -11,5 +11,5 @@
 let x = "Bar"
 
 in https://httpbin.org/headers
-     using [ { header = "Foo", value = x } ]
+     using [ { mapKey = "Foo", mapValue = x } ]
      as Text
diff --git a/dhall.cabal b/dhall.cabal
--- a/dhall.cabal
+++ b/dhall.cabal
@@ -1,5 +1,5 @@
 Name: dhall
-Version: 1.24.0
+Version: 1.25.0
 Cabal-Version: >=1.10
 Build-Type: Simple
 Tested-With: GHC == 7.10.3, GHC == 8.4.3, GHC == 8.6.1
@@ -102,8 +102,10 @@
     dhall-lang/Prelude/Text/concatSep
     dhall-lang/Prelude/Text/package.dhall
     dhall-lang/Prelude/Text/show
+    dhall-lang/tests/binary-decode/failure/unit/*.dhallb
     dhall-lang/tests/binary-decode/success/unit/*.dhall
     dhall-lang/tests/binary-decode/success/unit/*.dhallb
+    dhall-lang/tests/import/cache/dhall/1220efc43103e49b56c5bf089db8e0365bbfc455b8a2f0dc6ee5727a3586f85969fd
     dhall-lang/tests/import/data/*.txt
     dhall-lang/tests/import/data/*.dhall
     dhall-lang/tests/import/data/fieldOrder/*.dhall
@@ -243,8 +245,142 @@
     dhall-lang/tests/parser/success/unit/*.dhallb
     dhall-lang/tests/parser/success/unit/import/*.dhall
     dhall-lang/tests/parser/success/unit/import/*.dhallb
+    dhall-lang/tests/parser/success/unit/import/urls/*.dhall
+    dhall-lang/tests/parser/success/unit/import/urls/*.dhallb
     dhall-lang/tests/parser/success/text/*.dhall
     dhall-lang/tests/parser/success/text/*.dhallb
+    dhall-lang/tests/semantic-hash/success/*.dhall
+    dhall-lang/tests/semantic-hash/success/*.hash
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/*.dhall
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/access/*.hash
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/*.dhall
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/combineTypes/*.hash
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/*.dhall
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/prefer/*.hash
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/*.dhall
+    dhall-lang/tests/semantic-hash/success/haskell-tutorial/projection/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/and/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/and/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/build/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/build/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/even/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/even/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/fold/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/not/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/not/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/odd/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/or/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/or/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/show/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Bool/show/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Double/show/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Double/show/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Integer/show/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Integer/show/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Integer/toDouble/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/all/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/all/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/any/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/any/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/build/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/build/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/concat/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/concat/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/concatMap/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/filter/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/filter/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/fold/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/fold/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/generate/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/generate/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/head/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/head/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/indexed/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/indexed/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/iterate/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/iterate/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/last/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/last/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/length/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/length/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/map/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/map/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/null/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/null/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/replicate/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/replicate/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/reverse/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/reverse/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/shifted/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/shifted/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/List/unzip/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/List/unzip/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/build/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/build/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/enumerate/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/even/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/even/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/fold/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/isZero/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/odd/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/product/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/product/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/show/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/show/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/sum/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/toDouble/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Natural/toInteger/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/all/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/all/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/any/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/any/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/build/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/build/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/concat/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/filter/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/fold/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/head/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/head/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/last/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/last/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/length/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/length/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/map/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/map/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/null/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/null/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/toList/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Optional/unzip/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concat/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concat/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concatMap/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concatMapSep/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Text/concatSep/*.hash
+    dhall-lang/tests/semantic-hash/success/prelude/Text/show/*.dhall
+    dhall-lang/tests/semantic-hash/success/prelude/Text/show/*.hash
+    dhall-lang/tests/semantic-hash/success/simple/*.dhall
+    dhall-lang/tests/semantic-hash/success/simple/*.hash
+    dhall-lang/tests/semantic-hash/success/simplifications/*.dhall
+    dhall-lang/tests/semantic-hash/success/simplifications/*.hash
     dhall-lang/tests/typecheck/data/*.dhall
     dhall-lang/tests/typecheck/failure/*.dhall
     dhall-lang/tests/typecheck/success/*.dhall
@@ -367,8 +503,12 @@
     dhall-lang/tests/typecheck/success/prelude/Text/concatSep/*.dhall
     dhall-lang/tests/typecheck/success/simple/access/*.dhall
     dhall-lang/tests/typecheck/success/simple/*.dhall
+    dhall-lang/tests/type-inference/success/simple/*.dhall
+    dhall-lang/tests/type-inference/success/unit/*.dhall
     tests/format/*.dhall
     tests/lint/success/*.dhall
+    tests/diff/*.dhall
+    tests/diff/*.txt
     tests/regression/*.dhall
     tests/tutorial/*.dhall
 
@@ -400,17 +540,19 @@
         containers                  >= 0.5.0.0  && < 0.7 ,
         contravariant                              < 1.6 ,
         cryptonite                  >= 0.23     && < 1.0 ,
+        deepseq                                    < 1.5 ,
         Diff                        >= 0.2      && < 0.4 ,
         directory                   >= 1.2.2.0  && < 1.4 ,
         dotgen                      >= 0.4.2    && < 0.5 ,
+        either,
         exceptions                  >= 0.8.3    && < 0.11,
         filepath                    >= 1.4      && < 1.5 ,
         haskeline                   >= 0.7.2.1  && < 0.8 ,
-        lens-family-core            >= 1.0.0    && < 1.3 ,
+        lens-family-core            >= 1.0.0    && < 2.1 ,
         megaparsec                  >= 6.5.0    && < 7.1 ,
         memory                      >= 0.14     && < 0.15,
         mtl                         >= 2.2.1    && < 2.3 ,
-        optparse-applicative        >= 0.14.0.0 && < 0.15,
+        optparse-applicative        >= 0.14.0.0 && < 0.16,
         parsers                     >= 0.12.4   && < 0.13,
         prettyprinter               >= 1.2.0.1  && < 1.3 ,
         prettyprinter-ansi-terminal >= 1.1.1    && < 1.2 ,
@@ -420,6 +562,7 @@
         scientific                  >= 0.3.0.0  && < 0.4 ,
         template-haskell                           < 2.15,
         text                        >= 0.11.1.0 && < 1.3 ,
+        th-lift-instances           >= 0.1.13   && < 0.2 ,
         transformers                >= 0.2.0.0  && < 0.6 ,
         transformers-compat         >= 0.6.2    && < 0.7 ,
         unordered-containers        >= 0.1.3.0  && < 0.3 ,
@@ -482,6 +625,8 @@
         Dhall.Set,
         Dhall.Src,
         Dhall.Parser,
+        Dhall.Parser.Expression,
+        Dhall.Parser.Token,
         Dhall.Pretty,
         Dhall.Repl,
         Dhall.Tutorial,
@@ -491,12 +636,11 @@
             Dhall.TH
     Other-Modules:
         Dhall.Pretty.Internal,
-        Dhall.Parser.Expression,
         Dhall.Parser.Combinators,
-        Dhall.Parser.Token,
         Dhall.Import.Types,
         Dhall.Eval,
         Dhall.Util,
+        Dhall.X
         Paths_dhall
     if flag(with-http)
       Other-Modules:
@@ -519,6 +663,7 @@
     GHC-Options: -Wall
     Other-Modules:
         Dhall.Test.Dhall
+        Dhall.Test.Diff
         Dhall.Test.Format
         Dhall.Test.Import
         Dhall.Test.Lint
@@ -526,8 +671,10 @@
         Dhall.Test.Parser
         Dhall.Test.QuickCheck
         Dhall.Test.Regression
+        Dhall.Test.SemanticHash
         Dhall.Test.Tutorial
         Dhall.Test.TypeCheck
+        Dhall.Test.TypeInference
         Dhall.Test.Util
     Build-Depends:
         base                      >= 4        && < 5   ,
@@ -539,11 +686,14 @@
         directory                                      ,
         filepath                                       ,
         foldl                                    < 1.5 ,
+        lens-family-core          >= 1.0.0    && < 2.1 ,
         megaparsec                                     ,
         prettyprinter                                  ,
         QuickCheck                >= 2.10     && < 2.14,
         quickcheck-instances      >= 0.3.12   && < 0.4 ,
+        semigroups                                     ,
         serialise                                      ,
+        spoon                                    < 0.4 ,
         tasty                     >= 0.11.2   && < 1.3 ,
         tasty-hunit               >= 0.9.2    && < 0.11,
         tasty-quickcheck          >= 0.9.2    && < 0.11,
@@ -551,6 +701,9 @@
         transformers                                   ,
         turtle                                   < 1.6 ,
         vector                    >= 0.11.0.0 && < 0.13
+    if flag(with-http)
+      CPP-Options:
+        -DWITH_HTTP
     Default-Language: Haskell2010
 
 Test-Suite doctest
@@ -579,9 +732,9 @@
         base                      >= 4        && < 5  ,
         bytestring                                    ,
         containers                >= 0.5.0.0  && < 0.7,
-        criterion                 >= 1.1      && < 1.6,
         dhall                                         ,
         directory                                     ,
+        gauge                     >= 0.2.3    && < 0.3,
         serialise                                     ,
         text                      >= 0.11.1.0 && < 1.3
     Default-Language: Haskell2010
@@ -596,27 +749,6 @@
     Build-Depends:
         base                      >= 4        && < 5  ,
         containers                >= 0.5.0.0  && < 0.7,
-        criterion                 >= 1.1      && < 1.6,
-        dhall
-    Default-Language: Haskell2010
-
-Benchmark dhall-command
-    Type: exitcode-stdio-1.0
-    Main-Is: Main.hs
-    Hs-Source-Dirs: benchmark/dhall-command
-    Build-Depends:
-        base                      >= 4        && < 5  ,
-        dhall
-    Default-Language: Haskell2010
-    ghc-options: -rtsopts -O2
-
-Benchmark map-operations
-    Type: exitcode-stdio-1.0
-    Main-Is: Main.hs
-    Hs-Source-Dirs: benchmark/map
-    Build-Depends:
-        base                      >= 4        && < 5  ,
-        criterion                 >= 1.1      && < 1.6,
-        dhall
+        dhall                                         ,
+        gauge                     >= 0.2.3    && < 0.3
     Default-Language: Haskell2010
-    ghc-options: -rtsopts -O2
diff --git a/src/Dhall.hs b/src/Dhall.hs
--- a/src/Dhall.hs
+++ b/src/Dhall.hs
@@ -39,12 +39,19 @@
     , detailed
 
     -- * Types
-    , Type(..)
+    , Type (..)
     , RecordType(..)
     , UnionType(..)
     , InputType(..)
     , Interpret(..)
     , InvalidType(..)
+    , ExtractErrors(..)
+    , Extractor
+    , MonadicExtractor
+    , typeError
+    , extractError
+    , toMonadic
+    , fromMonadic
     , auto
     , genericAuto
     , InterpretOptions(..)
@@ -96,15 +103,18 @@
     , Generic
     ) where
 
-import Control.Applicative (empty, liftA2, (<|>), Alternative)
+import Control.Applicative (empty, liftA2, Alternative)
 import Control.Exception (Exception)
 import Control.Monad.Trans.State.Strict
 import Control.Monad (guard)
 import Data.Coerce (coerce)
+import Data.Either.Validation (Validation(..), ealt, eitherToValidation, validationToEither)
 import Data.Functor.Contravariant (Contravariant(..), (>$<), Op(..))
 import Data.Functor.Contravariant.Divisible (Divisible(..), divided)
+import Data.List.NonEmpty (NonEmpty (..))
 import Data.Monoid ((<>))
 import Data.Scientific (Scientific)
+import Data.Semigroup (Semigroup)
 import Data.Sequence (Seq)
 import Data.Text (Text)
 import Data.Text.Prettyprint.Doc (Pretty)
@@ -128,6 +138,8 @@
 import qualified Data.Foldable
 import qualified Data.Functor.Compose
 import qualified Data.Functor.Product
+import qualified Data.Maybe
+import qualified Data.List.NonEmpty
 import qualified Data.Semigroup
 import qualified Data.Scientific
 import qualified Data.Sequence
@@ -150,19 +162,76 @@
 -- >>> :set -XOverloadedStrings
 -- >>> :set -XRecordWildCards
 
+type Extractor s a = Validation (ExtractErrors s a)
+type MonadicExtractor s a = Either (ExtractErrors s a)
+
+
+typeError :: Expr s a -> Expr s a -> Extractor s a b
+typeError expected actual = Failure . ExtractErrors . pure . TypeMismatch $ InvalidType expected actual
+
+extractError :: Text -> Extractor s a b
+extractError = Failure . ExtractErrors . pure . ExtractError
+
+-- | Switches from an @Applicative@ extraction result, able to accumulate errors,
+-- to a @Monad@ extraction result, able to chain sequential operations
+toMonadic :: Extractor s a b -> MonadicExtractor s a b
+toMonadic = validationToEither
+
+-- | Switches from a @Monad@ extraction result, able to chain sequential errors,
+-- to an @Applicative@ extraction result, able to accumulate errors
+fromMonadic :: MonadicExtractor s a b -> Extractor s a b
+fromMonadic = eitherToValidation
+
+newtype ExtractErrors s a = ExtractErrors
+   { getErrors :: NonEmpty (ExtractError s a)
+   } deriving Semigroup
+
+instance (Pretty s, Pretty a, Typeable s, Typeable a) => Show (ExtractErrors s a) where
+    show (ExtractErrors (e :| [])) = show e
+    show (ExtractErrors es) = prefix <> (unlines . Data.List.NonEmpty.toList . fmap show $ es)
+      where
+        prefix =
+            "Multiple errors were encountered during extraction: \n\
+            \                                                    \n"
+
+instance (Pretty s, Pretty a, Typeable s, Typeable a) => Exception (ExtractErrors s a)
+
+{-| Extraction of a value can fail for two reasons, either a type mismatch (which should not happen,
+    as expressions are type-checked against the expected type before being passed to @extract@), or
+    a term-level error, described with a freeform text value.
+-}
+data ExtractError s a =
+    TypeMismatch (InvalidType s a)
+  | ExtractError Text
+
+instance (Pretty s, Pretty a, Typeable s, Typeable a) => Show (ExtractError s a) where
+  show (TypeMismatch e)  = show e
+  show (ExtractError es) =
+      _ERROR <> ": Failed extraction                                                   \n\
+      \                                                                                \n\
+      \The expression type-checked successfully but the transformation to the target   \n\
+      \type failed with the following error:                                           \n\
+      \                                                                                \n\
+      \" <> Data.Text.unpack es <> "\n\
+      \                                                                                \n"
+
+instance (Pretty s, Pretty a, Typeable s, Typeable a) => Exception (ExtractError s a)
+
 {-| Every `Type` must obey the contract that if an expression's type matches the
-    the `expected` type then the `extract` function must succeed.  If not, then
-    this exception is thrown
+    the `expected` type then the `extract` function must not fail with a type error.
+    If not, then this value is returned.
 
-    This exception indicates that an invalid `Type` was provided to the `input`
+    This value indicates that an invalid `Type` was provided to the `input`
     function
 -}
 data InvalidType s a = InvalidType
-  { invalidTypeExpected :: Expr s a
+  { invalidTypeExpected   :: Expr s a
   , invalidTypeExpression :: Expr s a
   }
   deriving (Typeable)
 
+instance (Pretty s, Typeable s, Pretty a, Typeable a) => Exception (InvalidType s a)
+
 _ERROR :: String
 _ERROR = "\ESC[1;31mError\ESC[0m"
 
@@ -185,9 +254,6 @@
           txt0 = Dhall.Util.insert invalidTypeExpected
           txt1 = Dhall.Util.insert invalidTypeExpression
 
-
-instance (Pretty s, Pretty a, Typeable s, Typeable a) => Exception (InvalidType s a)
-
 -- | @since 1.16
 data InputSettings = InputSettings
   { _rootDirectory :: FilePath
@@ -362,9 +428,8 @@
     let normExpr = Dhall.Core.normalizeWith (view normalizer settings) expr'
 
     case extract normExpr  of
-        Just x  -> return x
-        Nothing -> Control.Exception.throwIO
-                     (InvalidType expected expr')
+        Success x  -> return x
+        Failure e -> Control.Exception.throwIO e
 
 {-| Type-check and evaluate a Dhall program that is read from the
     file-system.
@@ -465,8 +530,8 @@
     -- ^ The decoded value in Haskell
 rawInput (Type {..}) expr = do
     case extract (Dhall.Core.normalize expr) of
-        Just x  -> pure x
-        Nothing -> empty
+        Success x  -> pure x
+        Failure _e -> empty
 
 {-| Use this to provide more detailed error messages
 
@@ -601,7 +666,7 @@
 > input :: Type a -> Text -> IO a
 -}
 data Type a = Type
-    { extract  :: Expr Src X -> Maybe a
+    { extract  :: Expr Src X -> Extractor Src X a
     -- ^ Extracts Haskell value from the Dhall expression
     , expected :: Expr Src X
     -- ^ Dhall type of the Haskell value
@@ -617,7 +682,7 @@
 bool = Type {..}
   where
     extract (BoolLit b) = pure b
-    extract  _          = Nothing
+    extract expr        = typeError expected expr
 
     expected = Bool
 
@@ -630,7 +695,7 @@
 natural = Type {..}
   where
     extract (NaturalLit n) = pure n
-    extract  _             = empty
+    extract  expr             = typeError Natural expr
 
     expected = Natural
 
@@ -643,7 +708,7 @@
 integer = Type {..}
   where
     extract (IntegerLit n) = pure n
-    extract  _             = empty
+    extract  expr          = typeError Integer expr
 
     expected = Integer
 
@@ -664,7 +729,7 @@
 double = Type {..}
   where
     extract (DoubleLit n) = pure n
-    extract  _            = empty
+    extract  expr            = typeError Double expr
 
     expected = Double
 
@@ -677,7 +742,7 @@
 lazyText = Type {..}
   where
     extract (TextLit (Chunks [] t)) = pure (Data.Text.Lazy.fromStrict t)
-    extract  _                      = empty
+    extract  expr = typeError Text expr
 
     expected = Text
 
@@ -698,8 +763,8 @@
 maybe (Type extractIn expectedIn) = Type extractOut expectedOut
   where
     extractOut (Some e    ) = fmap Just (extractIn e)
-    extractOut (App None _) = return Nothing
-    extractOut _            = empty
+    extractOut (App None _) = pure Nothing
+    extractOut expr         = typeError expectedOut expr
 
     expectedOut = App Optional expectedIn
 
@@ -712,7 +777,7 @@
 sequence (Type extractIn expectedIn) = Type extractOut expectedOut
   where
     extractOut (ListLit _ es) = traverse extractIn es
-    extractOut  _             = Nothing
+    extractOut expr           = typeError expectedOut expr
 
     expectedOut = App List expectedIn
 
@@ -741,8 +806,8 @@
 unit = Type extractOut expectedOut
   where
     extractOut (RecordLit fields)
-        | Data.Foldable.null fields = return ()
-    extractOut _ = Nothing
+        | Data.Foldable.null fields = pure ()
+    extractOut expr = typeError (Record mempty) expr
 
     expectedOut = Record mempty
 
@@ -763,10 +828,10 @@
 pair :: Type a -> Type b -> Type (a, b)
 pair l r = Type extractOut expectedOut
   where
-    extractOut (RecordLit fields) =
-      (,) <$> ( Dhall.Map.lookup "_1" fields >>= extract l )
-          <*> ( Dhall.Map.lookup "_2" fields >>= extract r )
-    extractOut _ = Nothing
+    extractOut expr@(RecordLit fields) =
+      (,) <$> ( Data.Maybe.maybe (typeError expectedOut expr) (extract l) $ Dhall.Map.lookup "_1" fields)
+          <*> ( Data.Maybe.maybe (typeError expectedOut expr) (extract r) $ Dhall.Map.lookup "_2" fields)
+    extractOut expr = typeError expectedOut expr
 
     expectedOut =
         Record
@@ -833,9 +898,10 @@
       where
         normalizer_ = Just (inputNormalizer opts)
 
-        extractOut e = Just (\i -> case extractIn (Dhall.Core.normalizeWith normalizer_ (App e (embed i))) of
-            Just o  -> o
-            Nothing -> error "Interpret: You cannot decode a function if it does not have the correct type" )
+        -- ToDo
+        extractOut e = pure (\i -> case extractIn (Dhall.Core.normalizeWith normalizer_ (App e (embed i))) of
+            Success o  -> o
+            Failure _e -> error "Interpret: You cannot decode a function if it does not have the correct type" )
 
         expectedOut = Pi "_" declared expectedIn
 
@@ -902,7 +968,7 @@
 instance GenericInterpret V1 where
     genericAutoWith _ = pure Type {..}
       where
-        extract _ = Nothing
+        extract expr = typeError expected expr
 
         expected = Union mempty
 
@@ -973,11 +1039,13 @@
         nameR = constructorModifier (Data.Text.pack (conName nR))
 
         extract e0 = do
-          (name, e1, _) <- extractUnionConstructor e0
-          if
-            | name == nameL -> fmap (L1 . M1) (extractL e1)
-            | name == nameR -> fmap (R1 . M1) (extractR e1)
-            | otherwise     -> Nothing
+          case extractUnionConstructor e0 of
+            Just (name, e1, _) ->
+              if
+                | name == nameL -> fmap (L1 . M1) (extractL e1)
+                | name == nameR -> fmap (R1 . M1) (extractR e1)
+                | otherwise     -> typeError expected e0
+            _ -> typeError expected e0
 
         expected =
             Union
@@ -998,11 +1066,12 @@
 
         name = constructorModifier (Data.Text.pack (conName n))
 
-        extract u = do
-          (name', e, _) <- extractUnionConstructor u
-          if
-            | name == name' -> fmap (R1 . M1) (extractR e)
-            | otherwise     -> fmap  L1       (extractL u)
+        extract u = case extractUnionConstructor u of
+          Just (name', e, _) ->
+            if
+              | name == name' -> fmap (R1 . M1) (extractR e)
+              | otherwise     -> fmap  L1       (extractL u)
+          Nothing -> typeError expected u
 
         expected =
             Union (Dhall.Map.insert name (notEmptyRecord expectedR) ktsL)
@@ -1020,11 +1089,12 @@
 
         name = constructorModifier (Data.Text.pack (conName n))
 
-        extract u = do
-          (name', e, _) <- extractUnionConstructor u
-          if
-            | name == name' -> fmap (L1 . M1) (extractL e)
-            | otherwise     -> fmap  R1       (extractR u)
+        extract u = case extractUnionConstructor u of
+          Just (name', e, _) ->
+            if
+              | name == name' -> fmap (L1 . M1) (extractL e)
+              | otherwise     -> fmap  R1       (extractR u)
+          _ -> typeError expected u
 
         expected =
             Union (Dhall.Map.insert name (notEmptyRecord expectedL) ktsR)
@@ -1037,7 +1107,7 @@
 instance (GenericInterpret (f :+: g), GenericInterpret (h :+: i)) => GenericInterpret ((f :+: g) :+: (h :+: i)) where
     genericAutoWith options = pure (Type {..})
       where
-        extract e = fmap L1 (extractL e) <|> fmap R1 (extractR e)
+        extract e = fmap L1 (extractL e) `ealt` fmap R1 (extractR e)
 
         expected = Union (Dhall.Map.union ktsL ktsR)
 
@@ -1055,7 +1125,7 @@
 instance GenericInterpret U1 where
     genericAutoWith _ = pure (Type {..})
       where
-        extract _ = Just U1
+        extract _ = pure U1
 
         expected = Record (Dhall.Map.fromList [])
 
@@ -1082,15 +1152,17 @@
 instance (Selector s, Interpret a) => GenericInterpret (M1 S s (K1 i a)) where
     genericAutoWith opts@(InterpretOptions {..}) = do
         name <- getSelName n
-        let extract (RecordLit m) = do
-                    let name' = fieldModifier (Data.Text.pack name)
-                    e <- Dhall.Map.lookup name' m
-                    fmap (M1 . K1) (extract' e)
-            extract _            = Nothing
         let expected =
                 Record (Dhall.Map.fromList [(key, expected')])
               where
                 key = fieldModifier (Data.Text.pack name)
+        let extract expr@(RecordLit m) =
+                    let name' = fieldModifier (Data.Text.pack name)
+                        extract'' e = fmap (M1 . K1) (extract' e)
+                        lookupRes = Dhall.Map.lookup name' m
+                        typeError' = typeError expected expr
+                    in Data.Maybe.maybe typeError' extract'' lookupRes
+            extract expr            = typeError expected expr
         pure (Type {..})
       where
         n :: M1 i s f a
@@ -1240,10 +1312,8 @@
 instance Inject a => Inject (Maybe a) where
     injectWith options = InputType embedOut declaredOut
       where
-        embedOut (Just x) =
-            OptionalLit declaredIn (pure (embedIn x))
-        embedOut Nothing =
-            OptionalLit declaredIn  empty
+        embedOut (Just x ) = Some (embedIn x)
+        embedOut  Nothing  = App None declaredIn
 
         InputType embedIn declaredIn = injectWith options
 
@@ -1494,7 +1564,7 @@
         )
         ( Data.Functor.Compose.Compose
             ( (->) ( Expr Src X ) )
-            Maybe
+            (Extractor Src X)
         )
         a
     )
@@ -1514,15 +1584,12 @@
 
 -- | Parse a single field of a record.
 field :: Text -> Type a -> RecordType a
-field key valueType =
+field key valueType@(Type extract expected) =
   let
-    extractBody expr = do
-      RecordLit fields <-
-        return expr
-
-      Dhall.Map.lookup key fields
-        >>= extract valueType
-
+    extractBody expr@(RecordLit fields) = case Dhall.Map.lookup key fields of
+      Just v -> extract v
+      _ -> typeError expected expr
+    extractBody expr = typeError expected expr
   in
     RecordType
       ( Data.Functor.Product.Pair
@@ -1589,15 +1656,16 @@
   where
     expect = (notEmptyRecord . Dhall.expected) <$> mp
 
-    extractF e0 = do
-      (fld, e1, rest) <- extractUnionConstructor e0
-
-      t <- Dhall.Map.lookup fld mp
+    extractF e0 =
+      let result = do
+            (fld, e1, rest) <- extractUnionConstructor e0
 
-      guard $ Dhall.Core.Union rest `Dhall.Core.judgmentallyEqual`
-                Dhall.Core.Union (Dhall.Map.delete fld expect)
+            t <- Dhall.Map.lookup fld mp
 
-      extract t e1
+            guard $ Dhall.Core.Union rest `Dhall.Core.judgmentallyEqual`
+                      Dhall.Core.Union (Dhall.Map.delete fld expect)
+            pure (t, e1)
+      in Data.Maybe.maybe (typeError (Union expect) e0) (uncurry extract) result
 
 -- | Parse a single constructor of a union
 constructor :: Text -> Type a -> UnionType a
diff --git a/src/Dhall/Binary.hs b/src/Dhall/Binary.hs
--- a/src/Dhall/Binary.hs
+++ b/src/Dhall/Binary.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE FlexibleInstances  #-}
 {-# LANGUAGE RecordWildCards    #-}
 {-# LANGUAGE OverloadedStrings  #-}
 
@@ -43,6 +44,7 @@
     , Var(..)
     )
 
+import Dhall.X (X(..))
 import Data.Foldable (toList)
 import Data.List.NonEmpty (NonEmpty(..))
 import Data.Monoid ((<>))
@@ -52,9 +54,11 @@
 import GHC.Float (double2Float, float2Double)
 
 import qualified Crypto.Hash
+import qualified Control.Monad       as Monad
 import qualified Data.ByteArray
 import qualified Data.ByteString
 import qualified Data.Sequence
+import qualified Dhall.Core
 import qualified Dhall.Map
 import qualified Dhall.Set
 import qualified Options.Applicative
@@ -126,11 +130,11 @@
 class ToTerm a where
     encode :: a -> Term
 
-instance ToTerm a => ToTerm (Expr s a) where
+instance ToTerm a => ToTerm (Expr X a) where
     encode (Var (V "_" n)) =
-        TInteger n
+        TInt n
     encode (Var (V x n)) =
-        TList [ TString x, TInteger n ]
+        TList [ TString x, TInt n ]
     encode NaturalBuild =
         TString "Natural/build"
     encode NaturalFold =
@@ -280,23 +284,15 @@
         l₁ = encode l₀
         r₁ = encode r₀
     encode (ListLit _T₀ xs₀)
-        | null xs₀  = TList [ TInt 4, _T₁ ]
+        | null xs₀  = TList [ TInt label, _T₁ ]
         | otherwise = TList ([ TInt 4, TNull ] ++ xs₁)
       where
-        _T₁ = case _T₀ of
-            Nothing -> TNull
-            Just t  -> encode t
+        (label, _T₁) = case _T₀ of
+            Nothing           -> (4 , TNull)
+            Just (App List t) -> (4 , encode t)
+            Just t            -> (28, encode t)
 
         xs₁ = map encode (Data.Foldable.toList xs₀)
-    encode (OptionalLit _T₀ Nothing) =
-        TList [ TInt 5, _T₁ ]
-      where
-        _T₁ = encode _T₀
-    encode (OptionalLit _T₀ (Just t₀)) =
-        TList [ TInt 5, _T₁, t₁ ]
-      where
-        _T₁ = encode _T₀
-        t₁  = encode t₀
     encode (Some t₀) =
         TList [ TInt 5, TNull, t₁ ]
       where
@@ -423,8 +419,16 @@
       where
         t₁  = encode t₀
         _T₁ = encode _T₀
-    encode (Note _ e) =
-        encode e
+    encode (ToMap t₀ Nothing) =
+        TList [ TInt 27, t₁ ]
+      where
+        t₁ = encode t₀
+    encode (ToMap t₀ (Just _T₀)) =
+        TList [ TInt 27, t₁, _T₁ ]
+      where
+        t₁  = encode t₀
+        _T₁ = encode _T₀
+    encode (Note (X absurd) _) = absurd
 
 instance ToTerm Import where
     encode import_ =
@@ -442,7 +446,7 @@
                     Nothing ->
                         TNull
                     Just h ->
-                        encode h
+                        encodeExpression h
 
                 scheme₁ = case scheme₀ of
                     HTTP  -> 0
@@ -485,21 +489,24 @@
                 Just digest ->
                     TBytes ("\x12\x20" <> Data.ByteArray.convert digest)
 
-            m = TInt (case importMode of Code -> 0; RawText -> 1)
+            m = TInt (case importMode of Code -> 0; RawText -> 1; Location -> 2;)
 
         Import {..} = import_
 
         ImportHashed {..} = importHashed
 
+instance ToTerm X where
+    encode = absurd
+
 -- | Types that can be decoded from a CBOR `Term`
 class FromTerm a where
     decode :: Term -> Maybe a
 
 instance FromTerm a => FromTerm (Expr s a) where
     decode (TInt n) =
-        return (Var (V "_" (fromIntegral n)))
-    decode (TInteger n) =
         return (Var (V "_" n))
+    decode (TInteger n) =
+        return (Var (V "_" (fromIntegral n)))
     decode (TString "Natural/build") =
         return NaturalBuild
     decode (TString "Natural/fold") =
@@ -564,19 +571,23 @@
         return (Const Sort)
     decode (TString "_") =
         empty
-    decode (TList [ TString x, TInt n ]) =
-        return (Var (V x (fromIntegral n)))
-    decode (TList [ TString x, TInteger n ]) =
+    decode (TList [ TString x, TInt n ]) = do
+        Monad.guard (x /= "_")
         return (Var (V x n))
+    decode (TList [ TString x, TInteger n ]) = do
+        Monad.guard (x /= "_")
+        return (Var (V x (fromIntegral n)))
     decode (TList (TInt 0 : f₁ : xs₁)) = do
         f₀  <- decode f₁
         xs₀ <- traverse decode xs₁
+        Monad.guard (not (null xs₀))
         return (foldl App f₀ xs₀)
     decode (TList [ TInt 1, _A₁, b₁ ]) = do
         _A₀ <- decode _A₁
         b₀  <- decode b₁
         return (Lam "_" _A₀ b₀)
     decode (TList [ TInt 1, TString x, _A₁, b₁ ]) = do
+        Monad.guard (x /= "_")
         _A₀ <- decode _A₁
         b₀  <- decode b₁
         return (Lam x _A₀ b₀)
@@ -585,6 +596,7 @@
         _B₀ <- decode _B₁
         return (Pi "_" _A₀ _B₀)
     decode (TList [ TInt 2, TString x, _A₁, _B₁ ]) = do
+        Monad.guard (x /= "_")
         _A₀ <- decode _A₁
         _B₀ <- decode _B₁
         return (Pi x _A₀ _B₀)
@@ -608,20 +620,13 @@
         return (op l₀ r₀)
     decode (TList [ TInt 4, _T₁ ]) = do
         _T₀ <- decode _T₁
-        return (ListLit (Just _T₀) empty)
+        return (ListLit (Just (App List _T₀)) empty)
     decode (TList (TInt 4 : TNull : xs₁ )) = do
         xs₀ <- traverse decode xs₁
         return (ListLit Nothing (Data.Sequence.fromList xs₀))
-    decode (TList [ TInt 5, _T₁ ]) = do
-        _T₀ <- decode _T₁
-        return (OptionalLit _T₀ Nothing)
     decode (TList [ TInt 5, TNull, t₁ ]) = do
         t₀ <- decode t₁
         return (Some t₀)
-    decode (TList [ TInt 5, _T₁, t₁ ]) = do
-        _T₀ <- decode _T₁
-        t₀  <- decode t₁
-        return (OptionalLit _T₀ (Just t₀))
     decode (TList [ TInt 6, t₁, u₁ ]) = do
         t₀ <- decode t₁
         u₀ <- decode u₁
@@ -717,6 +722,7 @@
 
         return (BoolIf t₀ l₀ r₀)
     decode (TList [ TInt 15, TInt n ]) = do
+        Monad.guard (0 <= n)
         return (NaturalLit (fromIntegral n))
     decode (TList [ TInt 15, TInteger n ]) = do
         return (NaturalLit (fromInteger n))
@@ -773,6 +779,16 @@
         t₀  <- decode t₁
         _T₀ <- decode _T₁
         return (Annot t₀ _T₀)
+    decode (TList [ TInt 27, t₁ ]) = do
+        t₀ <- decode t₁
+        return (ToMap t₀ Nothing)
+    decode (TList [ TInt 27, t₁, _T₁ ]) = do
+        t₀ <- decode t₁
+        _T₀ <- decode _T₁
+        return (ToMap t₀ (Just _T₀))
+    decode (TList [ TInt 28, _T₁ ]) = do
+        _T₀ <- decode _T₁
+        return (ListLit (Just _T₀) empty)
     decode _ =
         empty
 
@@ -801,6 +817,7 @@
         importMode <- case mode of
             0 -> return Code
             1 -> return RawText
+            2 -> return Location
             _ -> empty
 
         let remote scheme = do
@@ -876,6 +893,9 @@
 
     decode _ = empty
 
+instance FromTerm X where
+    decode _ = empty
+
 strip55799Tag :: Term -> Term
 strip55799Tag term =
     case term of
@@ -921,11 +941,14 @@
             TDouble a
 
 -- | Encode a Dhall expression as a CBOR `Term`
+--
+-- This 'Dhall.Core.denote's the expression before encoding it. To encode an
+-- already denoted expression, it is more efficient to directly use 'encode'.
 encodeExpression :: Expr s Import -> Term
-encodeExpression = encode
+encodeExpression e = encode (Dhall.Core.denote e :: Expr X Import)
 
 -- | Decode a Dhall expression from a CBOR `Term`
-decodeExpression :: Term -> Either DecodingFailure (Expr s Import)
+decodeExpression :: FromTerm a => Term -> Either DecodingFailure (Expr s a)
 decodeExpression term =
     case decodeWithoutVersion <|> decodeWithVersion of
         Just expression -> Right expression
@@ -938,7 +961,13 @@
     -- For backwards compatibility with older expressions that have a version
     -- tag to ease the migration
     decodeWithVersion = do
-        TList [ TString _, taggedTerm ] <- return strippedTerm
+        TList [ TString version, taggedTerm ] <- return strippedTerm
+
+        -- "_" has never been a valid version string, and this ensures that we
+        -- don't interpret `[ "_", 0 ]` as the expression `_` (encoded as `0`)
+        -- tagged with a version string of `"_"`
+        Monad.guard (version /= "_")
+
         decode taggedTerm
 
 data DecodingFailure = CBORIsNotDhall Term
diff --git a/src/Dhall/Context.hs b/src/Dhall/Context.hs
--- a/src/Dhall/Context.hs
+++ b/src/Dhall/Context.hs
@@ -57,7 +57,7 @@
 > lookup k n (insert k v c) = lookup k (n - 1) c
 > lookup k n (insert j v c) = lookup k  n      c  -- k /= j
 -}
-lookup :: Text -> Integer -> Context a -> Maybe a
+lookup :: Text -> Int -> Context a -> Maybe a
 lookup _ _ (Context         []  ) =
     Nothing
 lookup x n (Context ((k, v):kvs)) =
diff --git a/src/Dhall/Core.hs b/src/Dhall/Core.hs
--- a/src/Dhall/Core.hs
+++ b/src/Dhall/Core.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE BangPatterns       #-}
 {-# LANGUAGE CPP                #-}
+{-# LANGUAGE DeriveAnyClass     #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveGeneric      #-}
 {-# LANGUAGE DeriveTraversable  #-}
@@ -46,6 +47,7 @@
     , isNormalized
     , isNormalizedWith
     , denote
+    , shallowDenote
     , freeIn
 
     -- * Pretty-printing
@@ -68,6 +70,7 @@
 import Control.Applicative (Applicative(..), (<$>))
 #endif
 import Control.Applicative (empty)
+import Control.DeepSeq (NFData)
 import Control.Exception (Exception)
 import Control.Monad.IO.Class (MonadIO(..))
 import Crypto.Hash (SHA256)
@@ -88,6 +91,8 @@
 import Dhall.Src (Src)
 import {-# SOURCE #-} Dhall.Pretty.Internal
 import GHC.Generics (Generic)
+import Instances.TH.Lift ()
+import Language.Haskell.TH.Syntax (Lift)
 import Numeric.Natural (Natural)
 import Prelude hiding (succ)
 
@@ -126,8 +131,10 @@
     Dhall is not a dependently typed language
 -}
 data Const = Type | Kind | Sort
-    deriving (Show, Eq, Ord, Data, Bounded, Enum, Generic)
+    deriving (Show, Eq, Ord, Data, Bounded, Enum, Generic, NFData)
 
+instance Lift Const
+
 instance Pretty Const where
     pretty = Pretty.unAnnotate . prettyConst
 
@@ -138,7 +145,7 @@
     @Directory { components = [ "baz", "bar", "foo" ] }@
 -}
 newtype Directory = Directory { components :: [Text] }
-    deriving (Eq, Generic, Ord, Show)
+    deriving (Eq, Generic, Ord, Show, NFData)
 
 instance Semigroup Directory where
     Directory components₀ <> Directory components₁ =
@@ -153,7 +160,7 @@
 data File = File
     { directory :: Directory
     , file      :: Text
-    } deriving (Eq, Generic, Ord, Show)
+    } deriving (Eq, Generic, Ord, Show, NFData)
 
 instance Pretty File where
     pretty (File {..}) =
@@ -174,7 +181,7 @@
     -- ^ Path relative to @..@
     | Home
     -- ^ Path relative to @~@
-    deriving (Eq, Generic, Ord, Show)
+    deriving (Eq, Generic, Ord, Show, NFData)
 
 instance Pretty FilePrefix where
     pretty Absolute = ""
@@ -182,7 +189,7 @@
     pretty Parent   = ".."
     pretty Home     = "~"
 
-data Scheme = HTTP | HTTPS deriving (Eq, Generic, Ord, Show)
+data Scheme = HTTP | HTTPS deriving (Eq, Generic, Ord, Show, NFData)
 
 data URL = URL
     { scheme    :: Scheme
@@ -190,7 +197,7 @@
     , path      :: File
     , query     :: Maybe Text
     , headers   :: Maybe (Expr Src Import)
-    } deriving (Eq, Generic, Ord, Show)
+    } deriving (Eq, Generic, Ord, Show, NFData)
 
 instance Pretty URL where
     pretty (URL {..}) =
@@ -228,7 +235,7 @@
     | Env  Text
     -- ^ Environment variable
     | Missing
-    deriving (Eq, Generic, Ord, Show)
+    deriving (Eq, Generic, Ord, Show, NFData)
 
 parent :: File
 parent = File { directory = Directory { components = [ ".." ] }, file = "" }
@@ -266,13 +273,14 @@
     pretty Missing = "missing"
 
 -- | How to interpret the import's contents (i.e. as Dhall code or raw text)
-data ImportMode = Code | RawText deriving (Eq, Generic, Ord, Show)
+data ImportMode = Code | RawText | Location
+  deriving (Eq, Generic, Ord, Show, NFData)
 
 -- | A `ImportType` extended with an optional hash for semantic integrity checks
 data ImportHashed = ImportHashed
     { hash       :: Maybe (Crypto.Hash.Digest SHA256)
     , importType :: ImportType
-    } deriving (Eq, Generic, Ord, Show)
+    } deriving (Eq, Generic, Ord, Show, NFData)
 
 instance Semigroup ImportHashed where
     ImportHashed _ importType₀ <> ImportHashed hash importType₁ =
@@ -288,7 +296,7 @@
 data Import = Import
     { importHashed :: ImportHashed
     , importMode   :: ImportMode
-    } deriving (Eq, Generic, Ord, Show)
+    } deriving (Eq, Generic, Ord, Show, NFData)
 
 instance Semigroup Import where
     Import importHashed₀ _ <> Import importHashed₁ code =
@@ -299,8 +307,9 @@
       where
         suffix :: Text
         suffix = case importMode of
-            RawText -> " as Text"
-            Code    -> ""
+            RawText  -> " as Text"
+            Location -> " as Location"
+            Code     -> ""
 
 {-| Label for a bound variable
 
@@ -334,9 +343,11 @@
     Zero indices are omitted when pretty-printing `Var`s and non-zero indices
     appear as a numeric suffix.
 -}
-data Var = V Text !Integer
-    deriving (Data, Generic, Eq, Ord, Show)
+data Var = V Text !Int
+    deriving (Data, Generic, Eq, Ord, Show, NFData)
 
+instance Lift Var
+
 instance IsString Var where
     fromString str = V (fromString str) 0
 
@@ -422,7 +433,7 @@
     | TextShow
     -- | > List                                     ~  List
     | List
-    -- | > ListLit (Just t ) [x, y, z]              ~  [x, y, z] : List t
+    -- | > ListLit (Just t ) [x, y, z]              ~  [x, y, z] : t
     --   > ListLit  Nothing  [x, y, z]              ~  [x, y, z]
     | ListLit (Maybe (Expr s a)) (Seq (Expr s a))
     -- | > ListAppend x y                           ~  x # y
@@ -443,9 +454,6 @@
     | ListReverse
     -- | > Optional                                 ~  Optional
     | Optional
-    -- | > OptionalLit t (Just e)                   ~  [e] : Optional t
-    --   > OptionalLit t Nothing                    ~  []  : Optional t
-    | OptionalLit (Expr s a) (Maybe (Expr s a))
     -- | > Some e                                   ~  Some e
     | Some (Expr s a)
     -- | > None                                     ~  None
@@ -471,6 +479,9 @@
     -- | > Merge x y (Just t )                      ~  merge x y : t
     --   > Merge x y  Nothing                       ~  merge x y
     | Merge (Expr s a) (Expr s a) (Maybe (Expr s a))
+    -- | > ToMap x (Just t)                         ~  toMap x : t
+    --   > ToMap x  Nothing                         ~  toMap x
+    | ToMap (Expr s a) (Maybe (Expr s a))
     -- | > Field e x                                ~  e.x
     | Field (Expr s a) Text
     -- | > Project e (Left xs)                      ~  e.{ xs }
@@ -482,8 +493,12 @@
     | ImportAlt (Expr s a) (Expr s a)
     -- | > Embed import                             ~  import
     | Embed a
-    deriving (Eq, Ord, Foldable, Generic, Traversable, Show, Data)
+    deriving (Eq, Ord, Foldable, Generic, Traversable, Show, Data, NFData)
+-- NB: If you add a constructor to Expr, please also update the Arbitrary
+-- instance in Dhall.Test.QuickCheck.
 
+instance (Lift s, Lift a, Data s, Data a) => Lift (Expr s a)
+
 -- This instance is hand-written due to the fact that deriving
 -- it does not give us an INLINABLE pragma. We annotate this fmap
 -- implementation with this pragma below to allow GHC to, possibly,
@@ -536,7 +551,6 @@
   fmap _ ListIndexed = ListIndexed
   fmap _ ListReverse = ListReverse
   fmap _ Optional = Optional
-  fmap f (OptionalLit e maybeE) = OptionalLit (fmap f e) (fmap (fmap f) maybeE)
   fmap f (Some e) = Some (fmap f e)
   fmap _ None = None
   fmap _ OptionalFold = OptionalFold
@@ -549,6 +563,7 @@
   fmap f (CombineTypes e1 e2) = CombineTypes (fmap f e1) (fmap f e2)
   fmap f (Prefer e1 e2) = Prefer (fmap f e1) (fmap f e2)
   fmap f (Merge e1 e2 maybeE) = Merge (fmap f e1) (fmap f e2) (fmap (fmap f) maybeE)
+  fmap f (ToMap e maybeE) = ToMap (fmap f e) (fmap (fmap f) maybeE)
   fmap f (Field e1 v) = Field (fmap f e1) v
   fmap f (Project e1 vs) = Project (fmap f e1) (fmap (fmap f) vs)
   fmap f (Note s e1) = Note s (fmap f e1)
@@ -613,7 +628,6 @@
     ListIndexed          >>= _ = ListIndexed
     ListReverse          >>= _ = ListReverse
     Optional             >>= _ = Optional
-    OptionalLit a b      >>= k = OptionalLit (a >>= k) (fmap (>>= k) b)
     Some a               >>= k = Some (a >>= k)
     None                 >>= _ = None
     OptionalFold         >>= _ = OptionalFold
@@ -626,6 +640,7 @@
     CombineTypes a b     >>= k = CombineTypes (a >>= k) (b >>= k)
     Prefer a b           >>= k = Prefer (a >>= k) (b >>= k)
     Merge a b c          >>= k = Merge (a >>= k) (b >>= k) (fmap (>>= k) c)
+    ToMap a b            >>= k = ToMap (a >>= k) (fmap (>>= k) b)
     Field a b            >>= k = Field (a >>= k) b
     Project a b          >>= k = Project (a >>= k) (fmap (>>= k) b)
     Note a b             >>= k = Note a (b >>= k)
@@ -680,7 +695,6 @@
     first _  ListIndexed           = ListIndexed
     first _  ListReverse           = ListReverse
     first _  Optional              = Optional
-    first k (OptionalLit a b     ) = OptionalLit (first k a) (fmap (first k) b)
     first k (Some a              ) = Some (first k a)
     first _  None                  = None
     first _  OptionalFold          = OptionalFold
@@ -693,6 +707,7 @@
     first k (CombineTypes a b    ) = CombineTypes (first k a) (first k b)
     first k (Prefer a b          ) = Prefer (first k a) (first k b)
     first k (Merge a b c         ) = Merge (first k a) (first k b) (fmap (first k) c)
+    first k (ToMap a b           ) = ToMap (first k a) (fmap (first k) b)
     first k (Field a b           ) = Field (first k a) b
     first k (Project a b         ) = Project (first k a) (fmap (first k) b)
     first k (Note a b            ) = Note (k a) (first k b)
@@ -708,8 +723,10 @@
     { variable   :: Text
     , annotation :: Maybe (Expr s a)
     , value      :: Expr s a
-    } deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Ord, Data)
+    } deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Ord, Data, NFData)
 
+instance (Lift s, Lift a, Data s, Data a) => Lift (Binding s a)
+
 instance Bifunctor Binding where
     first k (Binding a b c) = Binding a (fmap (first k) b) (first k c)
 
@@ -717,8 +734,10 @@
 
 -- | The body of an interpolated @Text@ literal
 data Chunks s a = Chunks [(Text, Expr s a)] Text
-    deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Ord, Data)
+    deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Ord, Data, NFData)
 
+instance (Lift s, Lift a, Data s, Data a) => Lift (Chunks s a)
+
 instance Data.Semigroup.Semigroup (Chunks s a) where
     Chunks xysL zL <> Chunks         []    zR =
         Chunks xysL (zL <> zR)
@@ -811,7 +830,7 @@
     descend into a lambda or let expression that binds a variable of the same
     name in order to avoid shifting the bound variables by mistake.
 -}
-shift :: Integer -> Var -> Expr s a -> Expr s a
+shift :: Int -> Var -> Expr s a -> Expr s a
 shift _ _ (Const a) = Const a
 shift d (V x n) (Var (V x' n')) = Var (V x' n'')
   where
@@ -928,10 +947,6 @@
 shift _ _ ListIndexed = ListIndexed
 shift _ _ ListReverse = ListReverse
 shift _ _ Optional = Optional
-shift d v (OptionalLit a b) = OptionalLit a' b'
-  where
-    a' =       shift d v  a
-    b' = fmap (shift d v) b
 shift d v (Some a) = Some a'
   where
     a' = shift d v a
@@ -968,12 +983,17 @@
     a' =       shift d v  a
     b' =       shift d v  b
     c' = fmap (shift d v) c
+shift d v (ToMap a b) = ToMap a' b'
+  where
+    a' =       shift d v  a
+    b' = fmap (shift d v) b
 shift d v (Field a b) = Field a' b
   where
     a' = shift d v a
-shift d v (Project a b) = Project a' b
+shift d v (Project a b) = Project a' b'
   where
-    a' = shift d v a
+    a' =       shift d v  a
+    b' = fmap (shift d v) b
 shift d v (Note a b) = Note a b'
   where
     b' = shift d v b
@@ -1106,10 +1126,6 @@
 subst _ _ ListIndexed = ListIndexed
 subst _ _ ListReverse = ListReverse
 subst _ _ Optional = Optional
-subst x e (OptionalLit a b) = OptionalLit a' b'
-  where
-    a' =       subst x e  a
-    b' = fmap (subst x e) b
 subst x e (Some a) = Some a'
   where
     a' = subst x e a
@@ -1146,12 +1162,17 @@
     a' =       subst x e  a
     b' =       subst x e  b
     c' = fmap (subst x e) c
+subst x e (ToMap a b) = ToMap a' b'
+  where
+    a' =       subst x e  a
+    b' = fmap (subst x e) b
 subst x e (Field a b) = Field a' b
   where
     a' = subst x e a
-subst x e (Project a b) = Project a' b
+subst x e (Project a b) = Project a' b'
   where
-    a' = subst x e a
+    a' =       subst x e  a
+    b' = fmap (subst x e) b
 subst x e (Note a b) = Note a b'
   where
     b' = subst x e b
@@ -1184,8 +1205,8 @@
     expressions before normalizing them since normalization can convert an
     ill-typed expression into a well-typed expression.
 
-    However, `normalize` will not fail if the expression is ill-typed and will
-    leave ill-typed sub-expressions unevaluated.
+    `normalize` can also fail with `error` if you normalize an ill-typed
+    expression
 -}
 
 normalize :: Eq a => Expr s a -> Expr t a
@@ -1266,7 +1287,6 @@
 denote  ListIndexed           = ListIndexed
 denote  ListReverse           = ListReverse
 denote  Optional              = Optional
-denote (OptionalLit a b     ) = OptionalLit (denote a) (fmap denote b)
 denote (Some a              ) = Some (denote a)
 denote  None                  = None
 denote  OptionalFold          = OptionalFold
@@ -1279,11 +1299,16 @@
 denote (CombineTypes a b    ) = CombineTypes (denote a) (denote b)
 denote (Prefer a b          ) = Prefer (denote a) (denote b)
 denote (Merge a b c         ) = Merge (denote a) (denote b) (fmap denote c)
+denote (ToMap a b           ) = ToMap (denote a) (fmap denote b)
 denote (Field a b           ) = Field (denote a) b
 denote (Project a b         ) = Project (denote a) (fmap denote b)
 denote (ImportAlt a b       ) = ImportAlt (denote a) (denote b)
 denote (Embed a             ) = Embed a
 
+shallowDenote :: Expr s a -> Expr s a
+shallowDenote (Note _ e) = shallowDenote e
+shallowDenote         e  = e
+
 {-| Reduce an expression to its normal form, performing beta reduction and applying
     any custom definitions.
 
@@ -1298,11 +1323,19 @@
     That is, if the functions in custom context are not total then the Dhall language, evaluated
     with those functions is not total either.
 
+    `normalizeWith` can fail with an `error` if you normalize an ill-typed
+    expression
 -}
 normalizeWith :: Eq a => Maybe (ReifiedNormalizer a) -> Expr s a -> Expr t a
 normalizeWith (Just ctx) t = runIdentity (normalizeWithM (getReifiedNormalizer ctx) t)
 normalizeWith _          t = Dhall.Eval.nfEmpty t
 
+{-| This function generalizes `normalizeWith` by allowing the custom normalizer
+    to use an arbitrary `Monad`
+
+    `normalizeWithM` can fail with an `error` if you normalize an ill-typed
+    expression
+-}
 normalizeWithM
     :: (Monad m, Eq a) => NormalizerM m a -> Expr s a -> m (Expr t a)
 normalizeWithM ctx e0 = loop (denote e0)
@@ -1338,6 +1371,20 @@
                     -- build/fold fusion for `List`
                     App (App ListBuild _) (App (App ListFold _) e') -> loop e'
 
+                    App NaturalFold (NaturalLit n) -> do
+                        let natural = Var (V "natural" 0)
+                        let go 0  x = x
+                            go n' x = go (n'-1) (App (Var (V "succ" 0)) x)
+                        let n' = go n (Var (V "zero" 0))
+                        pure
+                            (Lam "natural"
+                                (Const Type)
+                                (Lam "succ"
+                                    (Pi "_" natural natural)
+                                    (Lam "zero"
+                                        natural
+                                        n')))
+
                     -- build/fold fusion for `Natural`
                     App NaturalBuild (App NaturalFold e') -> loop e'
 
@@ -1358,7 +1405,7 @@
                         lazyLoop !n = App succ' (lazyLoop (n - 1))
                     App NaturalBuild g -> loop (App (App (App g Natural) succ) zero)
                       where
-                        succ = Lam "x" Natural (NaturalPlus "x" (NaturalLit 1))
+                        succ = Lam "n" Natural (NaturalPlus "n" (NaturalLit 1))
 
                         zero = NaturalLit 0
                     App NaturalIsZero (NaturalLit n) -> pure (BoolLit (n == 0))
@@ -1395,7 +1442,7 @@
                                     (ListAppend (ListLit Nothing (pure "a")) "as")
                                 )
 
-                        nil = ListLit (Just _A₀) empty
+                        nil = ListLit (Just (App List _A₀)) empty
                     App (App (App (App (App ListFold _) (ListLit _ xs)) t) cons) nil -> do
                       t' <- loop t
                       if boundedType t' then strict else lazy
@@ -1421,7 +1468,7 @@
                         o = case Data.Sequence.viewr ys of
                                 _ :> y -> Some y
                                 _      -> App None t
-                    App (App ListIndexed _A₀) (ListLit _A₁ as₀) -> loop (ListLit t as₁)
+                    App (App ListIndexed _A₀) (ListLit _ as₀) -> loop (ListLit t as₁)
                       where
                         as₁ = Data.Sequence.mapWithIndex adapt as₀
 
@@ -1431,7 +1478,7 @@
                                   , ("value", _A₀)
                                   ]
 
-                        t | null as₀  = Just _A₂
+                        t | null as₀  = Just (App List _A₂)
                           | otherwise = Nothing
 
                         adapt n a_ =
@@ -1443,7 +1490,7 @@
                     App (App ListReverse t) (ListLit _ xs) ->
                         loop (ListLit m (Data.Sequence.reverse xs))
                       where
-                        m = if Data.Sequence.null xs then Just t else Nothing
+                        m = if Data.Sequence.null xs then Just (App List t) else Nothing
                     App (App (App (App (App OptionalFold _) (App None _)) _) _) nothing ->
                         loop nothing
                     App (App (App (App (App OptionalFold _) (Some x)) _) just) _ ->
@@ -1580,8 +1627,6 @@
     ListIndexed -> pure ListIndexed
     ListReverse -> pure ListReverse
     Optional -> pure Optional
-    OptionalLit _A Nothing -> loop (App None _A)
-    OptionalLit _ (Just a) -> loop (Some a)
     Some a -> Some <$> a'
       where
         a' = loop a
@@ -1661,6 +1706,30 @@
             _ -> Merge x' y' <$> t'
       where
         t' = traverse loop t
+    ToMap x t        -> do
+        x' <- loop x
+        t' <- traverse loop t
+        case x' of
+            RecordLit kvsX -> do
+                let entry (key, value) =
+                        RecordLit
+                            (Dhall.Map.fromList
+                                [ ("mapKey"  , TextLit (Chunks [] key))
+                                , ("mapValue", value                  )
+                                ]
+                            )
+
+                let keyValues = Data.Sequence.fromList (map entry (Dhall.Map.toList kvsX))
+
+                let listType = case t' of
+                        Just _ | null keyValues ->
+                            t'
+                        _ ->
+                            Nothing
+
+                return (ListLit listType keyValues)
+            _ -> do
+                return (ToMap x' t')
     Field r x        -> do
         r' <- loop r
         case r' of
@@ -1673,25 +1742,15 @@
         r' <- loop r
         case r' of
             RecordLit kvs ->
-                case traverse adapt (Dhall.Set.toList xs) of
-                    Just s  ->
-                        loop (RecordLit kvs')
-                      where
-                        kvs' = Dhall.Map.fromList s
-                    Nothing ->
-                        Project <$> (RecordLit <$> traverse loop kvs) <*> pure (Left xs)
-              where
-                adapt x = do
-                    v <- Dhall.Map.lookup x kvs
-                    return (x, v)
+                pure (RecordLit (Dhall.Map.restrictKeys kvs (Dhall.Set.toSet xs)))
             _   | null xs -> pure (RecordLit mempty)
-                | otherwise -> pure (Project r' (Left xs))
+                | otherwise -> pure (Project r' (Left (Dhall.Set.sort xs)))
     Project r (Right e1) -> do
         e2 <- loop e1
 
         case e2 of
             Record kts -> do
-                loop (Project r (Left (Dhall.Set.fromList (Dhall.Map.keys kts))))
+                loop (Project r (Left (Dhall.Set.fromSet (Dhall.Map.keysSet kts))))
             _ -> do
                 r' <- loop r
                 pure (Project r' (Right e2))
@@ -1715,6 +1774,9 @@
 
 {-| Returns `True` if two expressions are α-equivalent and β-equivalent and
     `False` otherwise
+
+    `judgmentallyEqual` can fail with an `error` if you compare ill-typed
+    expressions
 -}
 judgmentallyEqual :: Eq a => Expr s a -> Expr t a -> Bool
 judgmentallyEqual = Dhall.Eval.convEmpty
@@ -1734,10 +1796,19 @@
 --   Unlike `isNormalized`, this will fully normalize and traverse through the expression.
 --
 --   It is much more efficient to use `isNormalized`.
+--
+--  `isNormalizedWith` can fail with an `error` if you check an ill-typed
+--  expression
 isNormalizedWith :: (Eq s, Eq a) => Normalizer a -> Expr s a -> Bool
 isNormalizedWith ctx e = e == normalizeWith (Just (ReifiedNormalizer ctx)) e
 
 -- | Quickly check if an expression is in normal form
+--
+-- Given a well-typed expression @e@, @'isNormalized' e@ is equivalent to
+-- @e == 'normalize' e@.
+--
+-- Given an ill-typed expression, 'isNormalized' may fail with an error, or
+-- evaluate to either False or True!
 isNormalized :: Eq a => Expr s a -> Bool
 isNormalized e0 = loop (denote e0)
   where
@@ -1759,6 +1830,7 @@
           App (App OptionalBuild _) (App (App OptionalFold _) _) -> False
 
           App (App (App (App NaturalFold (NaturalLit _)) _) _) _ -> False
+          App NaturalFold (NaturalLit _) -> False
           App NaturalBuild _ -> False
           App NaturalIsZero (NaturalLit _) -> False
           App NaturalEven (NaturalLit _) -> False
@@ -1869,7 +1941,6 @@
       ListIndexed -> True
       ListReverse -> True
       Optional -> True
-      OptionalLit _ _ -> False
       Some a -> loop a
       None -> True
       OptionalFold -> True
@@ -1906,29 +1977,20 @@
                               Nothing -> True
                       _ -> True
               _ -> True
-      Field r x -> loop r &&
-          case r of
-              RecordLit kvs ->
-                  case Dhall.Map.lookup x kvs of
-                      Just _  -> False
-                      Nothing -> True
-              Union kvs ->
-                  case Dhall.Map.lookup x kvs of
-                      Just _  -> False
-                      Nothing -> True
-              _ -> True
-      Project r xs -> loop r &&
-          case r of
-              RecordLit kvs ->
-                  case xs of
-                      Left  s -> not (all (flip Dhall.Map.member kvs) s)
-                      Right e' ->
-                          case e' of
-                              Record kts ->
-                                  loop (Project r (Left (Dhall.Set.fromList (Dhall.Map.keys kts))))
-                              _ ->
-                                  False
-              _ -> not (null xs)
+      ToMap x t -> case x of
+          RecordLit _ -> False
+          _ -> loop x && all loop t
+      Field r _ -> case r of
+          RecordLit _ -> False
+          _ -> loop r
+      Project r p -> loop r &&
+          case p of
+              Left s -> case r of
+                  RecordLit _ -> False
+                  _ -> not (Dhall.Set.null s) && Dhall.Set.isSorted s
+              Right e' -> case e' of
+                  Record _ -> False
+                  _ -> loop e'
       Note _ e' -> loop e'
       ImportAlt l _r -> loop l
       Embed _ -> True
@@ -1943,8 +2005,9 @@
 False
 -}
 freeIn :: Eq a => Var -> Expr s a -> Bool
-variable `freeIn` expression =
-    Dhall.Core.shift 1 variable strippedExpression /= strippedExpression
+variable@(V var i) `freeIn` expression =
+    Dhall.Core.subst variable (Var (V var (i + 1))) strippedExpression
+      /= strippedExpression
   where
     denote' :: Expr t b -> Expr () b
     denote' = denote
@@ -1988,12 +2051,12 @@
         , "True"
         , "False"
         , "merge"
+        , "toMap"
         , "if"
         , "then"
         , "else"
         , "as"
         , "using"
-        , "constructors"
         , "Natural"
         , "Natural/fold"
         , "Natural/build"
@@ -2079,7 +2142,6 @@
 subExpressions _ ListIndexed = pure ListIndexed
 subExpressions _ ListReverse = pure ListReverse
 subExpressions _ Optional = pure Optional
-subExpressions f (OptionalLit a b) = OptionalLit <$> f a <*> traverse f b
 subExpressions f (Some a) = Some <$> f a
 subExpressions _ None = pure None
 subExpressions _ OptionalFold = pure OptionalFold
@@ -2093,8 +2155,9 @@
 subExpressions f (CombineTypes a b) = CombineTypes <$> f a <*> f b
 subExpressions f (Prefer a b) = Prefer <$> f a <*> f b
 subExpressions f (Merge a b t) = Merge <$> f a <*> f b <*> traverse f t
+subExpressions f (ToMap a t) = ToMap <$> f a <*> traverse f t
 subExpressions f (Field a b) = Field <$> f a <*> pure b
-subExpressions f (Project a b) = Project <$> f a <*> pure b
+subExpressions f (Project a b) = Project <$> f a <*> traverse f b
 subExpressions f (Note a b) = Note a <$> f b
 subExpressions f (ImportAlt l r) = ImportAlt <$> f l <*> f r
 subExpressions _ (Embed a) = pure (Embed a)
diff --git a/src/Dhall/Diff.hs b/src/Dhall/Diff.hs
--- a/src/Dhall/Diff.hs
+++ b/src/Dhall/Diff.hs
@@ -18,7 +18,6 @@
     ) where
 
 import Data.Foldable (fold, toList)
-import Data.Function (on)
 import Data.List.NonEmpty (NonEmpty(..))
 import Data.Monoid (Any(..))
 import Data.Semigroup
@@ -207,12 +206,15 @@
 diffInteger :: Integer -> Integer -> Diff
 diffInteger = diffPrimitive (token . Internal.prettyNumber)
 
+diffInt :: Int -> Int -> Diff
+diffInt = diffPrimitive (token . Internal.prettyInt)
+
 diffVar :: Var -> Var -> Diff
 diffVar (V xL nL) (V xR nR) = format mempty label <> "@" <> natural
   where
     label = diffLabel xL xR
 
-    natural = diffInteger nL nR
+    natural = diffInt nL nR
 
 diffPretty :: (Eq a, Pretty a) => a -> a -> Diff
 diffPretty = diffPrimitive (token . Pretty.pretty)
@@ -267,8 +269,8 @@
 diffKeysWith assign diffVals kvsL kvsR =
     diffFieldNames <> diffFieldValues <> (if anyEqual then [ ignore ] else [])
   where
-    ksL = Data.Set.fromList (Dhall.Map.keys kvsL)
-    ksR = Data.Set.fromList (Dhall.Map.keys kvsR)
+    ksL = Dhall.Map.keysSet kvsL
+    ksR = Dhall.Map.keysSet kvsR
 
     extraL = Data.Set.difference ksL ksR
     extraR = Data.Set.difference ksR ksL
@@ -570,8 +572,6 @@
     <>  " "
     <>  colon
     <>  " "
-    <>  builtin "List"
-    <>  " "
     <>  ignore
 skeleton (ListAppend {}) =
         ignore
@@ -579,18 +579,6 @@
     <>  operator "#"
     <>  " "
     <>  ignore
-skeleton (OptionalLit {}) =
-        lbracket
-    <>  " "
-    <>  ignore
-    <>  " "
-    <>  rbracket
-    <>  " "
-    <>  colon
-    <>  " "
-    <>  builtin "Optional"
-    <>  " "
-    <>  ignore
 skeleton (Record {}) =
         lbrace
     <>  " "
@@ -655,6 +643,10 @@
     <>  ignore
     <>  " "
     <>  ignore
+skeleton (ToMap {}) =
+        keyword "toMap"
+    <>  " "
+    <>  ignore
 skeleton (Field {}) =
         ignore
     <>  dot
@@ -770,29 +762,24 @@
     mismatch l r
 diffAnnotatedExpression l r@(Merge {}) =
     mismatch l r
+diffAnnotatedExpression (ToMap aL bL) (ToMap aR bR) = align doc
+  where
+    doc =   keyword "toMap"
+        <>  " "
+        <>  format " " (diffImportExpression aL aR)
+        <>  diffMaybe (colon <> " ") diffApplicationExpression bL bR
+diffAnnotatedExpression l@(ToMap {}) r =
+    mismatch l r
+diffAnnotatedExpression l r@(ToMap {}) =
+    mismatch l r
 diffAnnotatedExpression (ListLit aL@(Just _) bL) (ListLit aR bR) = align doc
   where
     doc =   format " " (diffList bL bR)
-        <>  format " " (diffMaybe (colon <> " ") (diffApplicationExpression `on` App List) aL aR)
+        <>  format " " (diffMaybe (colon <> " ") diffApplicationExpression aL aR)
 diffAnnotatedExpression (ListLit aL bL) (ListLit aR@(Just _) bR) = align doc
   where
     doc =   format " " (diffList bL bR)
-        <>  format " " (diffMaybe (colon <> " ") (diffApplicationExpression `on` App List) aL aR)
-diffAnnotatedExpression (OptionalLit aL bL) (OptionalLit aR bR) =
-    align doc
-  where
-    doc =   lbracket
-        <>  " "
-        <>  format " " (diffMaybe mempty diffExpression bL bR)
-        <>  rbracket
-        <>  " "
-        <>  colon
-        <>  " "
-        <>  diffApplicationExpression (App Optional aL) (App Optional aR)
-diffAnnotatedExpression l@(OptionalLit {}) r =
-    mismatch l r
-diffAnnotatedExpression l r@(OptionalLit {}) =
-    mismatch l r
+        <>  format " " (diffMaybe (colon <> " ") diffApplicationExpression aL aR)
 diffAnnotatedExpression l@(Annot {}) r@(Annot {}) =
     enclosed' "  " (colon <> " ") (docs l r)
   where
@@ -1166,10 +1153,6 @@
 diffPrimitiveExpression (ListLit Nothing bL) (ListLit Nothing bR) = align doc
   where
     doc = format " " (diffList bL bR)
-diffPrimitiveExpression l@(ListLit {}) r =
-    mismatch l r
-diffPrimitiveExpression l r@(ListLit {}) =
-    mismatch l r
 diffPrimitiveExpression ListBuild ListBuild =
     "…"
 diffPrimitiveExpression l@ListBuild r =
diff --git a/src/Dhall/Eval.hs b/src/Dhall/Eval.hs
--- a/src/Dhall/Eval.hs
+++ b/src/Dhall/Eval.hs
@@ -78,6 +78,7 @@
 -- import Dhall.Import.Types (InternalError)
 import Dhall.Map (Map)
 import Dhall.Set (Set)
+import Dhall.X   (X)
 import GHC.Natural (Natural)
 import Unsafe.Coerce (unsafeCoerce)
 
@@ -213,6 +214,7 @@
   | VCombineTypes !(Val a) !(Val a)
   | VPrefer !(Val a) !(Val a)
   | VMerge !(Val a) !(Val a) !(Maybe (Val a))
+  | VToMap !(Val a) !(Maybe (Val a))
   | VField !(Val a) !Text
   | VInject !(Map Text (Maybe (Val a))) !Text !(Maybe (Val a))
   | VProject !(Val a) !(Either (Set Text) (Val a))
@@ -251,7 +253,7 @@
 
 -- Out-of-env variables have negative de Bruijn levels.
 vVar :: Env a -> Var -> Val a
-vVar env (V x (fromInteger -> i :: Int)) = go env i where
+vVar env (V x i) = go env i where
   go (Extend env x' v) i
     | x == x'   = if i == 0 then v else go env (i - 1)
     | otherwise = go env i
@@ -436,7 +438,7 @@
                               `vApp` VHLam (Typed "a" a) (\x ->
                                               VHLam (Typed "as" (VList a)) (\as ->
                                                 vListAppend (VListLit Nothing (pure x)) as))
-                              `vApp` VListLit (Just a) mempty
+                              `vApp` VListLit (Just (VList a)) mempty
 
     ListFold         -> VPrim $ \a -> VPrim $ \case
                           VListLit _ as ->
@@ -464,8 +466,8 @@
     ListIndexed      -> VPrim $ \ a -> VPrim $ \case
                           VListLit _ as -> let
                             a' = if null as then
-                                   Just (VRecord (Dhall.Map.fromList
-                                                  [("index", VNatural), ("value", a)]))
+                                   Just (VList (VRecord (Dhall.Map.fromList
+                                                         [("index", VNatural), ("value", a)])))
                                  else
                                    Nothing
                             as' = Data.Sequence.mapWithIndex
@@ -481,7 +483,6 @@
                           t             -> VListReverse a t
 
     Optional         -> VPrim VOptional
-    OptionalLit a mt -> maybe (VNone (evalE a)) (\t -> VSome (evalE t)) mt
     Some t           -> VSome (evalE t)
     None             -> VPrim $ \ ~a -> VNone a
 
@@ -528,6 +529,16 @@
                             | Just f  <- Dhall.Map.lookup k m -> maybe f (vApp f) mt
                             | otherwise -> error errorMsg
                           (x, y, ma) -> VMerge x y ma
+    ToMap x ma       -> case (evalE x, evalE <$> ma) of
+                          (VRecordLit m, ma'@(Just _)) | null m ->
+                            VListLit ma' (Data.Sequence.empty)
+                          (VRecordLit m, _) -> let
+                            entry (k, v) =
+                              VRecordLit (Dhall.Map.fromList [("mapKey", VTextLit $ VChunks [] k),
+                                                              ("mapValue", v)])
+                            s = (Data.Sequence.fromList . map entry . Dhall.Map.toList) m
+                            in VListLit Nothing s
+                          (x, ma) -> VToMap x ma
     Field t k        -> case evalE t of
                           VRecordLit m
                             | Just v <- Dhall.Map.lookup k m -> v
@@ -541,15 +552,14 @@
                         if null ks then
                           VRecordLit mempty
                         else case evalE t of
-                          VRecordLit kvs
-                            | Just s <- traverse (\k -> (k,) <$> Dhall.Map.lookup k kvs) (toList ks)
-                              -> VRecordLit (Dhall.Map.sort (Dhall.Map.fromList s))
-                            | otherwise -> error errorMsg
-                          t -> VProject t (Left ks)
+                          VRecordLit kvs -> let
+                            kvs' = Dhall.Map.restrictKeys kvs (Dhall.Set.toSet ks)
+                            in VRecordLit (Dhall.Map.sort kvs')
+                          t -> VProject t (Left (Dhall.Set.sort ks))
     Project t (Right e) ->
                         case evalE e of
                           VRecord kts ->
-                            evalE (Project t (Left (Dhall.Set.fromList (Dhall.Map.keys kts))))
+                            evalE (Project t (Left (Dhall.Set.fromSet (Dhall.Map.keysSet kts))))
                           e' -> VProject (evalE t) (Right e')
     Note _ e         -> evalE e
     ImportAlt t _    -> evalE t
@@ -567,7 +577,9 @@
 {-# inline eqListBy #-}
 
 eqMapsBy :: Ord k => (v -> v -> Bool) -> Map k v -> Map k v -> Bool
-eqMapsBy f mL mR = eqListBy eq (Dhall.Map.toList mL) (Dhall.Map.toList mR)
+eqMapsBy f mL mR =
+    Dhall.Map.size mL == Dhall.Map.size mR
+    && eqListBy eq (Dhall.Map.toList mL) (Dhall.Map.toList mR)
   where
     eq (kL, vL) (kR, vR) = kL == kR && f vL vR
 {-# inline eqMapsBy #-}
@@ -656,8 +668,8 @@
     (VIntegerToDouble t , VIntegerToDouble t') -> convE t t'
 
     (VDouble       , VDouble)        -> True
-    (VDoubleLit n  , VDoubleLit n')  -> Dhall.Binary.encode (DoubleLit n  :: Expr Void Import) ==
-                                        Dhall.Binary.encode (DoubleLit n' :: Expr Void Import)
+    (VDoubleLit n  , VDoubleLit n')  -> Dhall.Binary.encode (DoubleLit n  :: Expr X Import) ==
+                                        Dhall.Binary.encode (DoubleLit n' :: Expr X Import)
     (VDoubleShow t , VDoubleShow t') -> convE t t'
 
     (VText, VText) -> True
@@ -692,6 +704,7 @@
     (VCombineTypes t u       , VCombineTypes t' u'         ) -> convE t t' && convE u u'
     (VPrefer  t u            , VPrefer t' u'               ) -> convE t t' && convE u u'
     (VMerge t u _            , VMerge t' u' _              ) -> convE t t' && convE u u'
+    (VToMap t _              , VToMap t' _                 ) -> convE t t'
     (VField t k              , VField t' k'                ) -> convE t t' && k == k'
     (VProject t (Left ks)    , VProject t' (Left ks')      ) -> convE t t' && ks == ks'
     (VProject t (Right e)    , VProject t' (Right e')      ) -> convE t t' && convE e e'
@@ -826,6 +839,7 @@
     VCombineTypes t u             -> CombineTypes (quoteE t) (quoteE u)
     VPrefer t u                   -> Prefer (quoteE t) (quoteE u)
     VMerge t u ma                 -> Merge (quoteE t) (quoteE u) (quoteE <$> ma)
+    VToMap t ma                   -> ToMap (quoteE t) (quoteE <$> ma)
     VField t k                    -> Field (quoteE t) k
     VProject t p                  -> Project (quoteE t) (fmap quoteE p)
     VInject m k Nothing           -> Field (Union ((quoteE <$>) <$> m)) k
@@ -852,7 +866,7 @@
 alphaNormalize :: Expr s a -> Expr s a
 alphaNormalize = goEnv NEmpty where
 
-  goVar :: Names -> Text -> Integer -> Expr s a
+  goVar :: Names -> Text -> Int -> Expr s a
   goVar e topX topI = go 0 e topI where
     go !acc (NBind env x) !i
       | x == topX = if i == 0 then Var (V "_" acc) else go (acc + 1) env (i - 1)
@@ -926,7 +940,6 @@
       ListIndexed      -> ListIndexed
       ListReverse      -> ListReverse
       Optional         -> Optional
-      OptionalLit a mt -> OptionalLit (go a) (go <$> mt)
       Some t           -> Some (go t)
       None             -> None
       OptionalFold     -> OptionalFold
@@ -939,8 +952,9 @@
       CombineTypes t u -> CombineTypes  (go t) (go u)
       Prefer t u       -> Prefer (go t) (go u)
       Merge x y ma     -> Merge (go x) (go y) (go <$> ma)
+      ToMap x ma       -> ToMap (go x) (go <$> ma)
       Field t k        -> Field (go t) k
-      Project t ks     -> Project (go t) ks
+      Project t ks     -> Project (go t) (go <$> ks)
       Note s e         -> Note s (go e)
       ImportAlt t u    -> ImportAlt (go t) (go u)
       Embed a          -> Embed a
diff --git a/src/Dhall/Freeze.hs b/src/Dhall/Freeze.hs
--- a/src/Dhall/Freeze.hs
+++ b/src/Dhall/Freeze.hs
@@ -77,14 +77,14 @@
     let normalizedExpression =
             Dhall.Core.alphaNormalize (Dhall.Core.normalize expression)
 
-    let expressionHash =
-            Just (Dhall.Import.hashExpression _standardVersion normalizedExpression)
+    -- make sure the frozen import is present in the semantic cache
+    Dhall.Import.writeExpressionToSemanticCache expression
 
-    let newImportHashed = (importHashed import_) { hash = expressionHash }
+    let expressionHash = Dhall.Import.hashExpression _standardVersion normalizedExpression
 
-    let newImport = import_ { importHashed = newImportHashed }
+    let newImportHashed = (importHashed import_) { hash = Just expressionHash }
 
-    State.evalStateT (Dhall.Import.exprToImport newImport normalizedExpression) status
+    let newImport = import_ { importHashed = newImportHashed }
 
     return newImport
 
diff --git a/src/Dhall/Import.hs b/src/Dhall/Import.hs
--- a/src/Dhall/Import.hs
+++ b/src/Dhall/Import.hs
@@ -100,23 +100,30 @@
 
 module Dhall.Import (
     -- * Import
-      exprFromImport
-    , exprToImport
-    , load
+      load
     , loadWith
+    , localToPath
     , hashExpression
     , hashExpressionToCode
+    , writeExpressionToSemanticCache
     , assertNoImports
     , Status
+    , Chained
+    , chainedImport
+    , chainedFromLocalHere
+    , chainedChangeMode
     , emptyStatus
     , stack
     , cache
-    , manager
+    , Depends(..)
+    , graph
+    , remote
+    , toHeaders
     , standardVersion
     , normalizer
     , startingContext
-    , resolver
-    , cacher
+    , chainImport
+    , ImportSemantics
     , Cycle(..)
     , ReferentiallyOpaque(..)
     , Imported(..)
@@ -130,12 +137,14 @@
 
 import Control.Applicative (Alternative(..))
 import Codec.CBOR.Term (Term(..))
-import Control.Exception (Exception, SomeException, throwIO, toException)
+import Control.Exception (Exception, SomeException, toException)
 import Control.Monad (guard)
-import Control.Monad.Catch (throwM, MonadCatch(catch), catches, Handler(..))
+import Control.Monad.Catch (throwM, MonadCatch(catch), handle)
 import Control.Monad.IO.Class (MonadIO(..))
+import Control.Monad.Trans.Class (lift)
 import Control.Monad.Trans.State.Strict (StateT)
 import Crypto.Hash (SHA256)
+import Data.ByteString (ByteString)
 import Data.CaseInsensitive (CI)
 import Data.List.NonEmpty (NonEmpty(..))
 import Data.Semigroup (Semigroup(..))
@@ -161,10 +170,10 @@
     , URL(..)
     )
 #ifdef MIN_VERSION_http_client
-import Dhall.Import.HTTP
+import Network.HTTP.Client (Manager)
+import Dhall.Import.HTTP hiding (HTTPHeader)
 #endif
 import Dhall.Import.Types
-import Text.Dot ((.->.), userNodeId)
 
 import Dhall.Parser (Parser(..), ParseError(..), Src(..), SourcedException(..))
 import Dhall.TypeCheck (X(..))
@@ -246,8 +255,8 @@
 
 -- | Extend another exception with the current import stack
 data Imported e = Imported
-    { importStack :: NonEmpty Import -- ^ Imports resolved so far, in reverse order
-    , nested      :: e               -- ^ The nested exception
+    { importStack :: NonEmpty Chained  -- ^ Imports resolved so far, in reverse order
+    , nested      :: e                 -- ^ The nested exception
     } deriving (Typeable)
 
 instance Exception e => Exception (Imported e)
@@ -311,12 +320,14 @@
 throwMissingImport :: (MonadCatch m, Exception e) => e -> m a
 throwMissingImport e = throwM (MissingImports [toException e])
 
+type HTTPHeader = (CI ByteString, ByteString)
+
 -- | Exception thrown when a HTTP url is imported but dhall was built without
 -- the @with-http@ Cabal flag.
 data CannotImportHTTPURL =
     CannotImportHTTPURL
         String
-        (Maybe [(CI Data.ByteString.ByteString, Data.ByteString.ByteString)])
+        (Maybe [HTTPHeader])
     deriving (Typeable)
 
 instance Exception CannotImportHTTPURL
@@ -335,7 +346,7 @@
 {-|
 > canonicalize . canonicalize = canonicalize
 
-> canonicalize (a <> b) = canonicalize a <> canonicalize b
+> canonicalize (a <> b) = canonicalize (canonicalize a <> canonicalize b)
 -}
 class Semigroup path => Canonicalize path where
     canonicalize :: path -> path
@@ -388,28 +399,6 @@
     canonicalize (Import importHashed importMode) =
         Import (canonicalize importHashed) importMode
 
-toHeaders
-  :: Expr s a
-  -> Maybe [(CI Data.ByteString.ByteString, Data.ByteString.ByteString)]
-toHeaders (ListLit _ hs) = do
-    hs' <- mapM toHeader hs
-    return (Data.Foldable.toList hs')
-toHeaders  _             = do
-    empty
-
-toHeader
-  :: Expr s a
-  -> Maybe (CI Data.ByteString.ByteString, Data.ByteString.ByteString)
-toHeader (RecordLit m) = do
-    TextLit (Chunks [] keyText  ) <- Dhall.Map.lookup "header" m
-    TextLit (Chunks [] valueText) <- Dhall.Map.lookup "value"  m
-    let keyBytes   = Data.Text.Encoding.encodeUtf8 keyText
-    let valueBytes = Data.Text.Encoding.encodeUtf8 valueText
-    return (Data.CaseInsensitive.mk keyBytes, valueBytes)
-toHeader _ = do
-    empty
-
-
 -- | Exception thrown when an integrity check fails
 data HashMismatch = HashMismatch
     { expectedHash :: Crypto.Hash.Digest SHA256
@@ -431,6 +420,8 @@
         <>  "\n"
         <>  "↳ " <> show actualHash <> "\n"
 
+-- | Construct the file path corresponding to a local import. If the import is
+--   _relative_ then the resulting path is also relative.
 localToPath :: MonadIO io => FilePrefix -> File -> io FilePath
 localToPath prefix file_ = liftIO $ do
     let File {..} = file_
@@ -445,11 +436,10 @@
             return "/"
 
         Parent -> do
-            pwd <- Directory.getCurrentDirectory
-            return (FilePath.takeDirectory pwd)
+            return ".."
 
         Here -> do
-            Directory.getCurrentDirectory
+            return "."
 
     let cs = map Text.unpack (file : components)
 
@@ -457,92 +447,300 @@
 
     return (foldr cons prefixPath cs)
 
--- | Parse an expression from a `Import` containing a Dhall program
-exprFromImport :: Import -> StateT (Status IO) IO Resolved
-exprFromImport here@(Import {..}) = do
-    let ImportHashed {..} = importHashed
+-- | Given a `Local` import construct the corresponding unhashed `Chained`
+--   import (interpreting relative path as relative to the current directory).
+chainedFromLocalHere :: FilePrefix -> File -> ImportMode -> Chained
+chainedFromLocalHere prefix file mode = Chained $
+     Import (ImportHashed Nothing (Local prefix (canonicalize file))) mode
 
-    Status {..} <- State.get
+-- | Adjust the import mode of a chained import
+chainedChangeMode :: ImportMode -> Chained -> Chained
+chainedChangeMode mode (Chained (Import importHashed _)) =
+    Chained (Import importHashed mode)
 
-    result <- Maybe.runMaybeT $ do
-        Just expectedHash <- return hash
-        cacheFile         <- getCacheFile expectedHash
-        True              <- liftIO (Directory.doesFileExist cacheFile)
+-- Chain imports, also typecheck and normalize headers if applicable.
+chainImport :: Chained -> Import -> StateT Status IO Chained
+chainImport (Chained parent) child@(Import importHashed@(ImportHashed _ (Remote url)) _) = do
+    url' <- normalizeHeaders url
+    let child' = child { importHashed = importHashed { importType = Remote url' } }
+    return (Chained (canonicalize (parent <> child')))
 
-        bytesStrict <- liftIO (Data.ByteString.readFile cacheFile)
+chainImport (Chained parent) child =
+    return (Chained (canonicalize (parent <> child)))
 
-        let actualHash = Crypto.Hash.hash bytesStrict
+-- | Load an import, resulting in a fully resolved, type-checked and normalised
+--   expression. @loadImport@ handles the 'hot' cache in @Status@ and defers to
+--   `loadImportWithSemanticCache` for imports that aren't in the @Status@ cache
+--   already.
+loadImport :: Chained -> StateT Status IO ImportSemantics
+loadImport import_ = do
+    Status {..} <- State.get
+    case Map.lookup import_ _cache of
+        Just importSemantics -> return importSemantics
+        Nothing -> do
+            importSemantics <- loadImportWithSemanticCache import_
+            zoom cache (State.modify (Map.insert import_ importSemantics))
+            return importSemantics
 
-        if expectedHash == actualHash
-            then return ()
-            else throwMissingImport (Imported _stack (HashMismatch {..}))
+-- | Load an import from the 'semantic cache'. Defers to
+--   `loadImportWithSemisemanticCache` for imports that aren't frozen (and
+--   therefore not cached semantically), as well as those that aren't cached yet.
+loadImportWithSemanticCache :: Chained -> StateT Status IO ImportSemantics
+loadImportWithSemanticCache
+  import_@(Chained (Import (ImportHashed Nothing _) _)) = do
+    loadImportWithSemisemanticCache import_
 
-        let bytesLazy = Data.ByteString.Lazy.fromStrict bytesStrict
+loadImportWithSemanticCache
+  import_@(Chained (Import (ImportHashed (Just semanticHash) _) _)) = do
+    Status { .. } <- State.get
+    mCached <- liftIO $ fetchFromSemanticCache semanticHash
 
-        term <- Dhall.Core.throws (Codec.Serialise.deserialiseOrFail bytesLazy)
+    case mCached of
+        Just bytesStrict -> do
+            let actualHash = Crypto.Hash.hash bytesStrict
+            if semanticHash == actualHash
+                then return ()
+                else do
+                    Status { _stack } <- State.get
+                    throwMissingImport (Imported _stack (HashMismatch {expectedHash = semanticHash, ..}))
 
-        Dhall.Core.throws (Dhall.Binary.decodeExpression term)
+            let bytesLazy = Data.ByteString.Lazy.fromStrict bytesStrict
+            term <- case Codec.Serialise.deserialiseOrFail bytesLazy of
+                Left err -> throwMissingImport (Imported _stack err)
+                Right t -> return t
+            importSemantics <- case Dhall.Binary.decodeExpression term of
+                Left err -> throwMissingImport (Imported _stack err)
+                Right sem -> return sem
 
-    case result of
-        Just resolvedExpression -> do
-            let newImport = here
+            return (ImportSemantics {..})
 
-            return (Resolved {..})
         Nothing -> do
-            exprFromUncachedImport here
+            ImportSemantics { importSemantics } <- loadImportWithSemisemanticCache import_
 
-{-| Save an expression to the specified `Import`
+            let variants = map (\version -> encodeExpression version (Dhall.Core.alphaNormalize importSemantics))
+                                [ minBound .. maxBound ]
+            case Data.Foldable.find ((== semanticHash). Crypto.Hash.hash) variants of
+                Just bytes -> liftIO $ writeToSemanticCache semanticHash bytes
+                Nothing -> do
+                    let expectedHash = semanticHash
+                    Status { _standardVersion, _stack } <- State.get
+                    let actualHash = hashExpression _standardVersion (Dhall.Core.alphaNormalize importSemantics)
+                    throwMissingImport (Imported _stack (HashMismatch {..}))
 
-    Currently this only works for cached imports and ignores other types of
-    imports, but could conceivably work for uncached imports in the future
+            return (ImportSemantics {..})
 
-    The main reason for this more general type is for symmetry with
-    `exprFromImport` and to support doing more clever things in the future,
-    like doing \"the right thing\" for uncached imports (i.e. exporting
-    environment variables or creating files)
--}
-exprToImport :: Import -> Expr Src X -> StateT (Status IO) IO ()
-exprToImport here expression = do
+-- Fetch encoded normal form from "semantic cache"
+fetchFromSemanticCache :: Crypto.Hash.Digest SHA256 -> IO (Maybe Data.ByteString.ByteString)
+fetchFromSemanticCache expectedHash = Maybe.runMaybeT $ do
+    cacheFile <- getCacheFile "dhall" expectedHash
+    True <- liftIO (Directory.doesFileExist cacheFile)
+    liftIO (Data.ByteString.readFile cacheFile)
+
+-- | Ensure that the given expression is present in the semantic cache. The
+--   given expression should be alpha-beta-normal.
+writeExpressionToSemanticCache :: Expr Src X -> IO ()
+writeExpressionToSemanticCache expression = writeToSemanticCache hash bytes
+  where
+    bytes = encodeExpression Dhall.Binary.defaultStandardVersion expression
+    hash = Crypto.Hash.hash bytes
+
+writeToSemanticCache :: Crypto.Hash.Digest SHA256 -> Data.ByteString.ByteString -> IO ()
+writeToSemanticCache hash bytes = do
+    _ <- Maybe.runMaybeT $ do
+        cacheFile <- getCacheFile "dhall" hash
+        liftIO (Data.ByteString.writeFile cacheFile bytes)
+    return ()
+
+-- Check the "semi-semantic" disk cache, otherwise typecheck and normalise from
+-- scratch.
+loadImportWithSemisemanticCache
+  :: Chained -> StateT Status IO ImportSemantics
+loadImportWithSemisemanticCache (Chained (Import (ImportHashed _ importType) Code)) = do
+    text <- fetchFresh importType
     Status {..} <- State.get
 
-    let Import {..} = here
+    path <- case importType of
+        Local prefix file -> liftIO $ do
+            path <- localToPath prefix file
+            absolutePath <- Directory.makeAbsolute path
+            return absolutePath
+        Remote url -> do
+            let urlText = Dhall.Pretty.Internal.pretty (url { headers = Nothing })
+            return (Text.unpack urlText)
+        Env env -> return $ Text.unpack env
+        Missing -> throwM (MissingImports [])
 
-    let ImportHashed {..} = importHashed
+    let parser = unParser $ do
+            Text.Parser.Token.whiteSpace
+            r <- Dhall.Parser.expr
+            Text.Parser.Combinators.eof
+            return r
 
-    _ <- Maybe.runMaybeT $ do
-        Just expectedHash  <- return hash
-        cacheFile          <- getCacheFile expectedHash
+    parsedImport <- case Text.Megaparsec.parse parser path text of
+        Left  errInfo -> do
+            throwMissingImport (Imported _stack (ParseError errInfo text))
+        Right expr    -> return expr
 
-        _ <- Dhall.Core.throws (Dhall.TypeCheck.typeWith _startingContext expression)
+    resolvedExpr <- loadWith parsedImport  -- we load imports recursively here
 
-        let normalizedExpression =
-                Dhall.Core.alphaNormalize
-                    (Dhall.Core.normalizeWith
-                        _normalizer
-                        expression
-                    )
+    -- Check the semi-semantic cache. See
+    -- https://github.com/dhall-lang/dhall-haskell/issues/1098 for the reasoning
+    -- behind semi-semantic caching.
+    let semisemanticHash = computeSemisemanticHash resolvedExpr
+    mCached <- lift $ fetchFromSemisemanticCache semisemanticHash
 
-        let check version = do
-                let bytes = encodeExpression version normalizedExpression
+    importSemantics <- case mCached of
+        Just bytesStrict -> do
+            let bytesLazy = Data.ByteString.Lazy.fromStrict bytesStrict
 
-                let actualHash = Crypto.Hash.hash bytes
+            term <- case Codec.Serialise.deserialiseOrFail bytesLazy of
+                Left err -> throwMissingImport (Imported _stack err)
+                Right t -> return t
 
-                guard (expectedHash == actualHash)
+            importSemantics <- case Dhall.Binary.decodeExpression term of
+                Left err -> throwMissingImport (Imported _stack err)
+                Right sem -> return sem
 
-                liftIO (Data.ByteString.writeFile cacheFile bytes)
+            return importSemantics
 
-        let fallback = do
-                let actualHash = hashExpression NoVersion normalizedExpression
+        Nothing -> do
+            betaNormal <- case Dhall.TypeCheck.typeWith _startingContext resolvedExpr of
+                Left  err -> throwMissingImport (Imported _stack err)
+                Right _ -> return (Dhall.Core.normalizeWith _normalizer resolvedExpr)
 
-                throwMissingImport (Imported _stack (HashMismatch {..}))
+            let bytes = encodeExpression _standardVersion betaNormal
+            lift $ writeToSemisemanticCache semisemanticHash bytes
 
-        Data.Foldable.asum (map check [ minBound .. maxBound ]) <|> fallback
+            return betaNormal
 
+    return (ImportSemantics {..})
+
+-- `as Text` imports aren't cached since they are well-typed and normal by
+-- construction
+loadImportWithSemisemanticCache (Chained (Import (ImportHashed _ importType) RawText)) = do
+    text <- fetchFresh importType
+
+    -- importSemantics is alpha-beta-normal by construction!
+    let importSemantics = TextLit (Chunks [] text)
+    return (ImportSemantics {..})
+
+-- `as Location` imports aren't cached since they are well-typed and normal by
+-- construction
+loadImportWithSemisemanticCache (Chained (Import (ImportHashed _ importType) Location)) = do
+    let locationType = Union $ Dhall.Map.fromList
+            [ ("Environment", Just Text)
+            , ("Remote", Just Text)
+            , ("Local", Just Text)
+            , ("Missing", Nothing)
+            ]
+
+    -- importSemantics is alpha-beta-normal by construction!
+    let importSemantics = case importType of
+            Missing -> Field locationType "Missing"
+            local@(Local _ _) ->
+                App (Field locationType "Local")
+                  (TextLit (Chunks [] (Dhall.Pretty.Internal.pretty local)))
+            remote_@(Remote _) ->
+                App (Field locationType "Remote")
+                  (TextLit (Chunks [] (Dhall.Pretty.Internal.pretty remote_)))
+            Env env ->
+                App (Field locationType "Environment")
+                  (TextLit (Chunks [] (Dhall.Pretty.Internal.pretty env)))
+
+    return (ImportSemantics {..})
+
+-- The semi-semantic hash of an expression is computed from the fully resolved
+-- AST (without normalising or type-checking it first). See
+-- https://github.com/dhall-lang/dhall-haskell/issues/1098 for further
+-- discussion.
+computeSemisemanticHash :: Expr Src X -> Crypto.Hash.Digest Crypto.Hash.SHA256
+computeSemisemanticHash resolvedExpr =
+    hashExpression Dhall.Binary.defaultStandardVersion resolvedExpr
+
+-- Fetch encoded normal form from "semi-semantic cache"
+fetchFromSemisemanticCache :: Crypto.Hash.Digest SHA256 -> IO (Maybe Data.ByteString.ByteString)
+fetchFromSemisemanticCache semisemanticHash = Maybe.runMaybeT $ do
+    cacheFile <- getCacheFile "dhall-haskell" semisemanticHash
+    True <- liftIO (Directory.doesFileExist cacheFile)
+    liftIO (Data.ByteString.readFile cacheFile)
+
+writeToSemisemanticCache :: Crypto.Hash.Digest SHA256 -> Data.ByteString.ByteString -> IO ()
+writeToSemisemanticCache semisemanticHash bytes = do
+    _ <- Maybe.runMaybeT $ do
+        cacheFile <- getCacheFile "dhall-haskell" semisemanticHash
+        liftIO (Data.ByteString.writeFile cacheFile bytes)
     return ()
 
+-- Fetch source code directly from disk/network
+fetchFresh :: ImportType -> StateT Status IO Text
+fetchFresh (Local prefix file) = do
+    Status { _stack } <- State.get
+    path <- liftIO $ localToPath prefix file
+    exists <- liftIO $ Directory.doesFileExist path
+    if exists
+        then liftIO $ Data.Text.IO.readFile path
+        else throwMissingImport (Imported _stack (MissingFile path))
+
+fetchFresh (Remote url) = do
+    Status { _remote } <- State.get
+    _remote url
+
+fetchFresh (Env env) = do
+    Status { _stack } <- State.get
+    x <- liftIO $ System.Environment.lookupEnv (Text.unpack env)
+    case x of
+        Just string -> do
+            return (Text.pack string)
+        Nothing -> do
+                throwMissingImport (Imported _stack (MissingEnvironmentVariable env))
+
+fetchFresh Missing = throwM (MissingImports [])
+
+
+fetchRemote :: URL -> StateT Status IO Data.Text.Text
+#ifndef MIN_VERSION_http_client
+fetchRemote (url@URL { headers = maybeHeadersExpression }) = do
+    let maybeHeaders = fmap toHeaders maybeHeadersExpression
+    let urlString = Text.unpack (Dhall.Core.pretty url)
+    Status { _stack } <- State.get
+    throwMissingImport (Imported _stack (CannotImportHTTPURL urlString maybeHeaders))
+#else
+fetchRemote url = do
+    manager <- liftIO $ newManager
+    zoom remote (State.put (fetchFromHTTP manager))
+    fetchFromHTTP manager url
+  where
+    fetchFromHTTP :: Manager -> URL -> StateT Status IO Data.Text.Text
+    fetchFromHTTP manager (url'@URL { headers = maybeHeadersExpression }) = do
+        let maybeHeaders = fmap toHeaders maybeHeadersExpression
+        fetchFromHttpUrl manager url' maybeHeaders
+#endif
+
+-- | Given a well-typed (of type `List { header : Text, value Text }` or
+-- `List { mapKey : Text, mapValue Text }`) headers expressions in normal form
+-- construct the corresponding binary http headers; otherwise return the empty
+-- list.
+toHeaders :: Expr s a -> [HTTPHeader]
+toHeaders (ListLit _ hs) = Data.Foldable.toList (Data.Foldable.fold maybeHeaders)
+  where
+      maybeHeaders = mapM toHeader hs
+toHeaders _ = []
+
+toHeader :: Expr s a -> Maybe HTTPHeader
+toHeader (RecordLit m) = do
+    TextLit (Chunks [] keyText  ) <-
+        Dhall.Map.lookup "header" m <|> Dhall.Map.lookup "mapKey" m
+    TextLit (Chunks [] valueText) <-
+        Dhall.Map.lookup "value" m <|> Dhall.Map.lookup "mapValue" m
+    let keyBytes   = Data.Text.Encoding.encodeUtf8 keyText
+    let valueBytes = Data.Text.Encoding.encodeUtf8 valueText
+    return (Data.CaseInsensitive.mk keyBytes, valueBytes)
+toHeader _ = do
+    empty
+
 getCacheFile
-    :: (Alternative m, MonadIO m) => Crypto.Hash.Digest SHA256 -> m FilePath
-getCacheFile hash = do
+    :: (Alternative m, MonadIO m) => FilePath -> Crypto.Hash.Digest SHA256 -> m FilePath
+getCacheFile cacheName hash = do
     let assertDirectory directory = do
             let private = transform Directory.emptyPermissions
                   where
@@ -573,11 +771,9 @@
 
     cacheDirectory <- getCacheDirectory
 
-    let dhallDirectory = cacheDirectory </> "dhall"
-
-    assertDirectory dhallDirectory
+    assertDirectory (cacheDirectory </> cacheName)
 
-    let cacheFile = dhallDirectory </> show hash
+    let cacheFile = (cacheDirectory </> cacheName) </> ("1220" <> show hash)
 
     return cacheFile
 
@@ -599,137 +795,77 @@
             Just homeDirectory -> return (homeDirectory </> ".cache")
             Nothing            -> empty
 
-exprFromUncachedImport :: Import -> StateT (Status IO) IO Resolved
-exprFromUncachedImport import_@(Import {..}) = do
-    let ImportHashed {..} = importHashed
-
-    (path, text, newImport) <- case importType of
-        Local prefix file -> liftIO $ do
-            path   <- localToPath prefix file
-            exists <- Directory.doesFileExist path
-
-            if exists
-                then return ()
-                else throwMissingImport (MissingFile path)
-
-            text <- Data.Text.IO.readFile path
-
-            return (path, text, import_)
-
-        Remote url@URL { headers = maybeHeadersExpression } -> do
-            maybeHeadersAndExpression <- case maybeHeadersExpression of
-                Nothing -> do
-                    return Nothing
-                Just headersExpression -> do
-                    expr <- loadWith headersExpression
-
-                    let expected :: Expr Src X
-                        expected =
-                            App List
-                                ( Record
-                                    ( Dhall.Map.fromList
-                                        [("header", Text), ("value", Text)]
-                                    )
-                                )
-                    let suffix_ = Dhall.Pretty.Internal.prettyToStrictText expected
-                    let annot = case expr of
-                            Note (Src begin end bytes) _ ->
-                                Note (Src begin end bytes') (Annot expr expected)
-                              where
-                                bytes' = bytes <> " : " <> suffix_
-                            _ ->
-                                Annot expr expected
-
-                    case Dhall.TypeCheck.typeOf annot of
-                        Left err -> liftIO (throwIO err)
-                        Right _  -> return ()
-
-                    let expr' = Dhall.Core.normalize expr
-
-                    case toHeaders expr' of
-                        Just headers -> do
-                            return (Just (headers, expr'))
-                        Nothing      -> do
-                            liftIO (throwIO InternalError)
-
-#ifdef MIN_VERSION_http_client
-            let maybeHeaders = fmap fst maybeHeadersAndExpression
-
-            let newHeaders =
-                    fmap (fmap absurd . snd) maybeHeadersAndExpression
-
-            (path, text) <- fetchFromHttpUrl url maybeHeaders
+-- If the URL contains headers typecheck them and replace them with their normal
+-- forms.
+normalizeHeaders :: URL -> StateT Status IO URL
+normalizeHeaders url@URL { headers = Just headersExpression } = do
+    Status { _stack } <- State.get
+    loadedExpr <- loadWith headersExpression
 
-            let newImport = Import
-                    { importHashed = ImportHashed
-                        { importType =
-                            Remote (url { headers = newHeaders })
-                        , ..
-                        }
-                    , ..
-                    }
+    let go key₀ key₁ = do
+            let expected :: Expr Src X
+                expected =
+                    App List
+                        ( Record
+                            ( Dhall.Map.fromList
+                                [ (key₀, Text), (key₁, Text) ]
+                            )
+                        )
 
-            return (path, text, newImport)
-#else
-            let urlString = Text.unpack (Dhall.Core.pretty url)
+            let suffix_ = Dhall.Pretty.Internal.prettyToStrictText expected
+            let annot = case loadedExpr of
+                    Note (Src begin end bytes) _ ->
+                        Note (Src begin end bytes') (Annot loadedExpr expected)
+                      where
+                        bytes' = bytes <> " : " <> suffix_
+                    _ ->
+                        Annot loadedExpr expected
 
-            liftIO (throwIO (CannotImportHTTPURL urlString mheaders))
-#endif
+            _ <- case (Dhall.TypeCheck.typeOf annot) of
+                Left err -> throwMissingImport (Imported _stack err)
+                Right _ -> return ()
 
-        Env env -> liftIO $ do
-            x <- System.Environment.lookupEnv (Text.unpack env)
-            case x of
-                Just string -> do
-                    return (Text.unpack env, Text.pack string, import_)
-                Nothing -> do
-                    throwMissingImport (MissingEnvironmentVariable env)
+            return (Dhall.Core.normalize loadedExpr)
 
-        Missing -> liftIO $ do
-            throwM (MissingImports [])
+    let handler₀ (e :: SomeException) = do
+            {- Try to typecheck using the preferred @mapKey@/@mapValue@ fields
+               and fall back to @header@/@value@ if that fails. However, if
+               @header@/@value@ still fails then re-throw the original exception
+               for @mapKey@ / @mapValue@. -}
+            let handler₁ (_ :: SomeException) =
+                    throwMissingImport (Imported _stack e)
 
-    case importMode of
-        Code -> do
-            let parser = unParser $ do
-                    Text.Parser.Token.whiteSpace
-                    r <- Dhall.Parser.expr
-                    Text.Parser.Combinators.eof
-                    return r
+            handle handler₁ (go "header" "value")
 
-            case Text.Megaparsec.parse parser path text of
-                Left errInfo -> do
-                    liftIO (throwIO (ParseError errInfo text))
-                Right resolvedExpression -> do
-                    return (Resolved {..})
+    headersExpression' <-
+        handle handler₀ (go "mapKey" "mapValue")
 
-        RawText -> do
-            let resolvedExpression = TextLit (Chunks [] text)
+    return url { headers = Just (fmap absurd headersExpression') }
 
-            return (Resolved {..})
+normalizeHeaders url = return url
 
 -- | Default starting `Status`, importing relative to the given directory.
-emptyStatus :: FilePath -> Status IO
-emptyStatus = emptyStatusWith exprFromImport exprToImport
+emptyStatus :: FilePath -> Status
+emptyStatus = emptyStatusWith fetchRemote
 
 {-| Generalized version of `load`
 
     You can configure the desired behavior through the initial `Status` that you
     supply
 -}
-loadWith :: MonadCatch m => Expr Src Import -> StateT (Status m) m (Expr Src X)
+loadWith :: Expr Src Import -> StateT Status IO (Expr Src X)
 loadWith expr₀ = case expr₀ of
   Embed import₀ -> do
     Status {..} <- State.get
 
     let parent = NonEmpty.head _stack
 
-    let import₁ = parent <> import₀
-
-    let child = canonicalize import₁
+    child <- chainImport parent import₀
 
-    let local (Import (ImportHashed _ (Remote  {})) _) = False
-        local (Import (ImportHashed _ (Local   {})) _) = True
-        local (Import (ImportHashed _ (Env     {})) _) = True
-        local (Import (ImportHashed _ (Missing {})) _) = True
+    let local (Chained (Import (ImportHashed _ (Remote  {})) _)) = False
+        local (Chained (Import (ImportHashed _ (Local   {})) _)) = True
+        local (Chained (Import (ImportHashed _ (Env     {})) _)) = True
+        local (Chained (Import (ImportHashed _ (Missing {})) _)) = True
 
     let referentiallySane = not (local child) || local parent
 
@@ -739,118 +875,22 @@
 
     let _stack' = NonEmpty.cons child _stack
 
-    expr <- if child `elem` _stack
+    if child `elem` _stack
         then throwMissingImport (Imported _stack (Cycle import₀))
-        else do
-            case Map.lookup child _cache of
-                Just (childNode, expr) -> do
-                    zoom dot . State.modify $ \getDot -> do
-                        parentNode <- getDot
-
-                        -- Add edge between parent and child
-                        parentNode .->. childNode
-
-                        -- Return parent NodeId
-                        pure parentNode
-
-                    pure expr
-                Nothing        -> do
-                    -- Here we have to match and unwrap the @MissingImports@
-                    -- in a separate handler, otherwise we'd have it wrapped
-                    -- in another @Imported@ when parsing a @missing@, because
-                    -- we are representing it with an empty exception list
-                    -- (which would not be empty if this would happen).
-                    -- TODO: restructure the Exception hierarchy to prevent
-                    -- this nesting from happening in the first place.
-                    let handler₀
-                            :: (MonadCatch m)
-                            => MissingImports
-                            -> StateT (Status m) m Resolved
-                        handler₀ (MissingImports es) =
-                          throwM
-                            (MissingImports
-                               (map
-                                 (\e -> toException (Imported _stack' e))
-                                 es
-                               )
-                             )
-
-                        handler₁
-                            :: (MonadCatch m)
-                            => SomeException
-                            -> StateT (Status m) m Resolved
-                        handler₁ e =
-                          throwMissingImport (Imported _stack' e)
-
-                    -- This loads a \"dynamic\" expression (i.e. an expression
-                    -- that might still contain imports)
-                    let loadDynamic = _resolver child
-
-                    Resolved {..} <- loadDynamic `catches` [ Handler handler₀, Handler handler₁ ]
-
-                    let stackWithNewImport = NonEmpty.cons newImport _stack
-
-                    let childNodeId = userNodeId _nextNodeId
-
-                    -- Increment the next node id
-                    zoom nextNodeId $ State.modify succ
-
-                    -- Make current node the dot graph
-                    zoom dot . State.put $ importNode childNodeId child
-
-                    zoom stack (State.put stackWithNewImport)
-                    expr'' <- loadWith resolvedExpression
-                    zoom stack (State.put _stack)
-
-                    zoom dot . State.modify $ \getSubDot -> do
-                        parentNode <- _dot
-
-                        -- Get current node back from sub-graph
-                        childNode <- getSubDot
-
-                        -- Add edge between parent and child
-                        parentNode .->. childNode
-
-                        -- Return parent NodeId
-                        pure parentNode
-
-                    _cacher child expr''
-
-                    -- Type-check expressions here for three separate reasons:
-                    --
-                    --  * to verify that they are closed
-                    --  * to catch type errors as early in the import process
-                    --    as possible
-                    --  * to avoid normalizing ill-typed expressions that need
-                    --    to be hashed
-                    --
-                    -- There is no need to check expressions that have been
-                    -- cached, since they have already been checked
-                    expr''' <- case Dhall.TypeCheck.typeWith _startingContext expr'' of
-                        Left  err -> throwM (Imported _stack' err)
-                        Right _   -> return (Dhall.Core.normalizeWith _normalizer expr'')
-                    zoom cache (State.modify' (Map.insert child (childNodeId, expr''')))
-                    return expr'''
+        else return ()
 
-    case hash (importHashed import₀) of
-        Nothing -> do
-            return ()
-        Just expectedHash -> do
-            let matches version =
-                    let actualHash =
-                            hashExpression version (Dhall.Core.alphaNormalize expr)
+    zoom graph . State.modify $
+        -- Add the edge `parent -> child` to the import graph
+        \edges -> Depends parent child : edges
 
-                    in  expectedHash == actualHash
+    let stackWithChild = NonEmpty.cons child _stack
 
-            if any matches [ minBound .. maxBound ]
-                then return ()
-                else do
-                    let actualHash =
-                            hashExpression NoVersion (Dhall.Core.alphaNormalize expr)
+    zoom stack (State.put stackWithChild)
+    ImportSemantics {..} <- loadImport child
+    zoom stack (State.put _stack)
 
-                    throwMissingImport (Imported _stack' (HashMismatch {..}))
+    return importSemantics
 
-    return expr
   ImportAlt a b -> loadWith a `catch` handler₀
     where
       handler₀ (SourcedException (Src begin _ text₀) (MissingImports es₀)) =
@@ -912,7 +952,6 @@
   Optional             -> pure Optional
   None                 -> pure None
   Some a               -> Some <$> loadWith a
-  OptionalLit a b      -> OptionalLit <$> loadWith a <*> mapM loadWith b
   OptionalFold         -> pure OptionalFold
   OptionalBuild        -> pure OptionalBuild
   Record a             -> Record <$> mapM loadWith a
@@ -923,6 +962,7 @@
   CombineTypes a b     -> CombineTypes <$> loadWith a <*> loadWith b
   Prefer a b           -> Prefer <$> loadWith a <*> loadWith b
   Merge a b c          -> Merge <$> loadWith a <*> loadWith b <*> mapM loadWith c
+  ToMap a b            -> ToMap <$> loadWith a <*> mapM loadWith b
   Field a b            -> Field <$> loadWith a <*> pure b
   Project a b          -> Project <$> loadWith a <*> mapM loadWith b
   Note a b             -> do
@@ -946,7 +986,7 @@
     intermediateExpression = fmap absurd expression
 
     term :: Term
-    term = Dhall.Binary.encode intermediateExpression
+    term = Dhall.Binary.encodeExpression intermediateExpression
 
     taggedTerm :: Term
     taggedTerm =
diff --git a/src/Dhall/Import/HTTP.hs b/src/Dhall/Import/HTTP.hs
--- a/src/Dhall/Import/HTTP.hs
+++ b/src/Dhall/Import/HTTP.hs
@@ -5,15 +5,13 @@
 module Dhall.Import.HTTP where
 
 import Control.Exception (Exception)
-import Control.Monad (join)
 import Control.Monad.IO.Class (MonadIO(..))
 import Control.Monad.Trans.State.Strict (StateT)
 import Data.ByteString (ByteString)
 import Data.CaseInsensitive (CI)
-import Data.Dynamic (fromDynamic, toDyn)
+import Data.Dynamic (toDyn)
 import Data.Semigroup ((<>))
 import Data.Text (Text)
-import Lens.Family.State.Strict (zoom)
 
 import Dhall.Core
     ( Directory(..)
@@ -51,7 +49,7 @@
 
 import qualified Network.HTTP.Client                     as HTTP
 import qualified Network.HTTP.Client.TLS                 as HTTP
-import qualified Network.HTTP.Types.Status
+import qualified Network.HTTP.Types
 
 mkPrettyHttpException :: HttpException -> PrettyHttpException
 mkPrettyHttpException ex =
@@ -88,7 +86,7 @@
             <>  "↳ " <> show statusCode
       where
         statusCode =
-            Network.HTTP.Types.Status.statusCode
+            Network.HTTP.Types.statusCode
                 (HTTP.responseStatus response)
     e' -> "\n" <> show e'
 #else
@@ -109,24 +107,17 @@
         <> show e'
 #endif
 
-needManager :: StateT (Status m) IO Manager
-needManager = do
-    x <- zoom manager State.get
-    case join (fmap fromDynamic x) of
-        Just m  -> return m
-        Nothing -> do
-            let settings = HTTP.tlsManagerSettings
-
+newManager :: IO Manager
+newManager = do
+    let settings = HTTP.tlsManagerSettings
 #ifdef MIN_VERSION_http_client
 #if MIN_VERSION_http_client(0,5,0)
-                    { HTTP.managerResponseTimeout = HTTP.responseTimeoutMicro (30 * 1000 * 1000) }  -- 30 seconds
+          { HTTP.managerResponseTimeout = HTTP.responseTimeoutMicro (30 * 1000 * 1000) }  -- 30 seconds
 #else
-                    { HTTP.managerResponseTimeout = Just (30 * 1000 * 1000) }  -- 30 seconds
+          { HTTP.managerResponseTimeout = Just (30 * 1000 * 1000) }  -- 30 seconds
 #endif
 #endif
-            m <- liftIO (HTTP.newManager settings)
-            zoom manager (State.put (Just (toDyn m)))
-            return m
+    HTTP.newManager settings
 
 data NotCORSCompliant = NotCORSCompliant
     { expectedOrigins :: [ByteString]
@@ -232,12 +223,15 @@
 
     queryText = foldMap renderQuery query
 
+type HTTPHeader = Network.HTTP.Types.Header
+
 fetchFromHttpUrl
-    :: URL
-    -> Maybe [(CI ByteString, ByteString)]
-    -> StateT (Status m) IO (String, Text.Text)
+    :: Manager
+    -> URL
+    -> Maybe [HTTPHeader]
+    -> StateT Status IO Text.Text
 #ifdef __GHCJS__
-fetchFromHttpUrl childURL Nothing = do
+fetchFromHttpUrl _ childURL Nothing = do
     let childURLText = renderURL childURL
 
     let childURLString = Text.unpack childURLText
@@ -250,15 +244,13 @@
         200 -> return ()
         _   -> fail (childURLString <> " returned a non-200 status code: " <> show statusCode)
 
-    return (childURLString, body)
-fetchFromHttpUrl _ _ = do
+    return body
+fetchFromHttpUrl _ _ _ = do
     fail "Dhall does not yet support custom headers when built using GHCJS"
 #else
-fetchFromHttpUrl childURL mheaders = do
+fetchFromHttpUrl manager childURL mheaders = do
     let childURLString = Text.unpack (renderURL childURL)
 
-    m <- needManager
-
     request <- liftIO (HTTP.parseUrlThrow childURLString)
 
     let requestWithHeaders =
@@ -266,7 +258,7 @@
               Nothing      -> request
               Just headers -> request { HTTP.requestHeaders = headers }
 
-    let io = HTTP.httpLbs requestWithHeaders m
+    let io = HTTP.httpLbs requestWithHeaders manager
 
     let handler e = do
             let _ = e :: HttpException
@@ -276,7 +268,7 @@
 
     Status {..} <- State.get
 
-    let parentImport = NonEmpty.head _stack
+    let Chained parentImport = NonEmpty.head _stack
 
     let parentImportType = importType (importHashed parentImport)
 
@@ -286,5 +278,5 @@
 
     case Data.Text.Lazy.Encoding.decodeUtf8' bytes of
         Left  err  -> liftIO (Control.Exception.throwIO err)
-        Right text -> return (childURLString, Data.Text.Lazy.toStrict text)
+        Right text -> return (Data.Text.Lazy.toStrict text)
 #endif
diff --git a/src/Dhall/Import/Types.hs b/src/Dhall/Import/Types.hs
--- a/src/Dhall/Import/Types.hs
+++ b/src/Dhall/Import/Types.hs
@@ -10,6 +10,7 @@
 import Data.List.NonEmpty (NonEmpty)
 import Data.Map (Map)
 import Data.Semigroup ((<>))
+import Data.Text.Prettyprint.Doc (Pretty(..))
 import Dhall.Binary (StandardVersion(..))
 import Dhall.Context (Context)
 import Dhall.Core
@@ -22,70 +23,69 @@
   , ImportMode (..)
   , ImportType (..)
   , ReifiedNormalizer(..)
-  , pretty
+  , URL
   )
 import Dhall.Parser (Src)
 import Dhall.TypeCheck (X)
 import Lens.Family (LensLike')
 import System.FilePath (isRelative, splitDirectories)
-import Text.Dot (Dot, NodeId, userNode, userNodeId)
 
 import qualified Dhall.Binary
 import qualified Dhall.Context
 import qualified Data.Map      as Map
 import qualified Data.Text
 
-data Resolved = Resolved
-    { resolvedExpression :: Expr Src Import
-    -- ^ Expression with its immediate imports resolved
-    , newImport          :: Import
-    -- ^ New import to use in place of the original import for chaining
-    --   downstream imports
+-- | A fully 'chained' import, i.e. if it contains a relative path that path is
+--   relative to the current directory. If it is a remote import with headers
+--   those are well-typed (either of type `List { header : Text, value Text}` or
+--   `List { mapKey : Text, mapValue Text})` and in normal form. These
+--   invariants are preserved by the API exposed by @Dhall.Import@.
+newtype Chained = Chained { chainedImport :: Import }
+  deriving (Eq, Ord)
+
+instance Pretty Chained where
+    pretty (Chained import_) = pretty import_
+
+data ImportSemantics = ImportSemantics
+    { importSemantics :: Expr Src X
+    -- ^ The fully resolved import, typechecked and beta-normal.
     }
 
+-- | `parent` imports (i.e. depends on) `child`
+data Depends = Depends { parent :: Chained, child :: Chained }
+
 -- | State threaded throughout the import process
-data Status m = Status
-    { _stack :: NonEmpty Import
+data Status = Status
+    { _stack :: NonEmpty Chained
     -- ^ Stack of `Import`s that we've imported along the way to get to the
     -- current point
 
-    , _dot :: Dot NodeId
-    -- ^ Graph of all the imports visited so far
-
-    , _nextNodeId :: Int
-    -- ^ Next node id to be used for the dot graph generation
+    , _graph :: [Depends]
+    -- ^ Graph of all the imports visited so far, represented by a list of
+    --   import dependencies.
 
-    , _cache :: Map Import (NodeId, Expr Src X)
+    , _cache :: Map Chained ImportSemantics
     -- ^ Cache of imported expressions with their node id in order to avoid
     --   importing the same expression twice with different values
 
-    , _manager :: Maybe Dynamic
-    -- ^ Cache for the HTTP `Manager` so that we only acquire it once
+    , _remote :: URL -> StateT Status IO Data.Text.Text
+    -- ^ The remote resolver, fetches the content at the given URL.
 
     , _standardVersion :: StandardVersion
 
     , _normalizer :: Maybe (ReifiedNormalizer X)
 
     , _startingContext :: Context (Expr Src X)
-
-    , _resolver :: Import -> StateT (Status m) m Resolved
-
-    , _cacher :: Import -> Expr Src X -> StateT (Status m) m ()
     }
 
--- | Default starting `Status` that is polymorphic in the base `Monad`
-emptyStatusWith
-    :: (Import -> StateT (Status m) m Resolved)
-    -> (Import -> Expr Src X -> StateT (Status m) m ())
-    -> FilePath
-    -> Status m
-emptyStatusWith _resolver _cacher rootDirectory = Status {..}
+-- | Initial `Status`, parameterised over the remote resolver, importing
+--   relative to the given directory.
+emptyStatusWith :: (URL -> StateT Status IO Data.Text.Text) -> FilePath -> Status
+emptyStatusWith _remote rootDirectory = Status {..}
   where
-    _stack = pure rootImport
-
-    _dot = importNode (userNodeId 0) rootImport
+    _stack = pure (Chained rootImport)
 
-    _nextNodeId = 1
+    _graph = []
 
     _cache = Map.empty
 
@@ -114,51 +114,28 @@
       , importMode = Code
       }
 
-importNode :: NodeId -> Import -> Dot NodeId
-importNode nodeId i = do
-    userNode
-        nodeId
-        [ ("label", Data.Text.unpack $ pretty i)
-        , ("shape", "box")
-        , ("style", "rounded")
-        ]
-    pure nodeId
-
-stack :: Functor f => LensLike' f (Status m) (NonEmpty Import)
+stack :: Functor f => LensLike' f Status (NonEmpty Chained)
 stack k s = fmap (\x -> s { _stack = x }) (k (_stack s))
 
-dot :: Functor f => LensLike' f (Status m) (Dot NodeId)
-dot k s = fmap (\x -> s { _dot = x }) (k (_dot s))
-
-nextNodeId :: Functor f => LensLike' f (Status m) Int
-nextNodeId k s = fmap (\x -> s { _nextNodeId = x }) (k (_nextNodeId s))
+graph :: Functor f => LensLike' f Status [Depends]
+graph k s = fmap (\x -> s { _graph = x }) (k (_graph s))
 
-cache :: Functor f => LensLike' f (Status m) (Map Import (NodeId, Expr Src X))
+cache :: Functor f => LensLike' f Status (Map Chained ImportSemantics)
 cache k s = fmap (\x -> s { _cache = x }) (k (_cache s))
 
-manager :: Functor f => LensLike' f (Status m) (Maybe Dynamic)
-manager k s = fmap (\x -> s { _manager = x }) (k (_manager s))
+remote :: Functor f => LensLike' f Status (URL -> StateT Status IO Data.Text.Text)
+remote k s = fmap (\x -> s { _remote = x }) (k (_remote s))
 
-standardVersion :: Functor f => LensLike' f (Status m) StandardVersion
+standardVersion :: Functor f => LensLike' f Status StandardVersion
 standardVersion k s =
     fmap (\x -> s { _standardVersion = x }) (k (_standardVersion s))
 
-normalizer :: Functor f => LensLike' f (Status m) (Maybe (ReifiedNormalizer X))
+normalizer :: Functor f => LensLike' f Status (Maybe (ReifiedNormalizer X))
 normalizer k s = fmap (\x -> s {_normalizer = x}) (k (_normalizer s))
 
-startingContext :: Functor f => LensLike' f (Status m) (Context (Expr Src X))
+startingContext :: Functor f => LensLike' f Status (Context (Expr Src X))
 startingContext k s =
     fmap (\x -> s { _startingContext = x }) (k (_startingContext s))
-
-resolver
-    :: Functor f
-    => LensLike' f (Status m) (Import -> StateT (Status m) m Resolved)
-resolver k s = fmap (\x -> s { _resolver = x }) (k (_resolver s))
-
-cacher
-    :: Functor f
-    => LensLike' f (Status m) (Import -> Expr Src X -> StateT (Status m) m ())
-cacher k s = fmap (\x -> s { _cacher = x }) (k (_cacher s))
 
 {-| This exception indicates that there was an internal error in Dhall's
     import-related logic
diff --git a/src/Dhall/Lint.hs b/src/Dhall/Lint.hs
--- a/src/Dhall/Lint.hs
+++ b/src/Dhall/Lint.hs
@@ -3,12 +3,8 @@
 module Dhall.Lint
     ( -- * Lint
       lint
-    , removeLetInLet
-    , removeUnusedBindings
-    , optionalLitToSomeNone
     ) where
 
-import Control.Monad (mplus)
 import Data.List.NonEmpty (NonEmpty(..))
 import Data.Semigroup ((<>))
 import Dhall.Core (Binding(..), Expr(..), Import, Var(..), subExpressions)
@@ -22,40 +18,34 @@
 
     * removes unused @let@ bindings with 'removeLetInLet'.
     * consolidates nested @let@ bindings to use a multiple-@let@ binding with 'removeUnusedBindings'.
-    * switches legacy @List@-like @Optional@ literals to use @Some@ / @None@ instead with 'optionalLitToSomeNone'
 -}
 lint :: Expr s Import -> Expr t Import
-lint =
-  Dhall.Optics.rewriteOf
-    subExpressions
-    ( \e ->
-                removeLetInLet e
-        `mplus` removeUnusedBindings e
-        `mplus` optionalLitToSomeNone e
-    )
-    . Dhall.Core.denote
+lint = postproc . linting . preproc
+  where
+    -- pre-processing: remove Note constructors and unfold Let blocks
+    preproc = Dhall.Optics.rewriteOf subExpressions unfoldNestedLets . Dhall.Core.denote
+    -- main linting step: remove unused let bindings and update optional syntax
+    linting = Dhall.Optics.rewriteOf subExpressions removeUnusedBindings
+    -- post-processing: fold nested Lets into Let blocks
+    postproc = Dhall.Optics.rewriteOf subExpressions removeLetInLet
 
+-- Merge nested Let blocks. Only finds immediately nested blocks -- use
+-- Dhall.Core.denote to make sure there aren't any Note constructors in the way!
 removeLetInLet :: Eq a => Expr s a -> Maybe (Expr s a)
 removeLetInLet (Let a (Let b c)) = Just (Let (a <> b) c)
 removeLetInLet _ = Nothing
 
+-- Remove unused Let bindings. Only considers Let blocks binding a single
+-- variable -- unfold Let blocks first to make sure we don't miss any rewrite
+-- opportunities!
 removeUnusedBindings :: Eq a => Expr s a -> Maybe (Expr s a)
 removeUnusedBindings (Let (Binding a _ _ :| []) d)
     | not (V a 0 `Dhall.Core.freeIn` d) =
-        Just d
-    | otherwise =
-        Nothing
-removeUnusedBindings (Let (Binding a _ _ :| (l : ls)) d)
-    | not (V a 0 `Dhall.Core.freeIn` e) =
-        Just e
-    | otherwise =
-        Nothing
-  where
-    e = Let (l :| ls) d
+        Just (Dhall.Core.shift (-1) (V a 0) d)
 removeUnusedBindings _ = Nothing
 
-
-optionalLitToSomeNone :: Expr s a -> Maybe (Expr s a)
-optionalLitToSomeNone (OptionalLit _ (Just b)) = Just (Some b)
-optionalLitToSomeNone (OptionalLit a Nothing) = Just (App None a)
-optionalLitToSomeNone _ = Nothing
+-- Unfold Let blocks into nested Let bindings binding a single variable each.
+-- Pre-processing step before applying the removeUnusedBindings rule.
+unfoldNestedLets :: Expr s a -> Maybe (Expr s a)
+unfoldNestedLets (Let (b :| (l : ls)) d) = Just (Let (b :| []) (Let (l :| ls) d))
+unfoldNestedLets _ = Nothing
diff --git a/src/Dhall/Main.hs b/src/Dhall/Main.hs
--- a/src/Dhall/Main.hs
+++ b/src/Dhall/Main.hs
@@ -21,14 +21,15 @@
 
 import Control.Applicative (optional, (<|>))
 import Control.Exception (SomeException)
+import Data.List.NonEmpty (NonEmpty(..))
 import Data.Monoid ((<>))
 import Data.Text (Text)
 import Data.Text.Prettyprint.Doc (Doc, Pretty)
 import Data.Version (showVersion)
 import Dhall.Binary (StandardVersion)
-import Dhall.Core (Expr(..), Import)
+import Dhall.Core (Expr(Annot), Import, pretty)
 import Dhall.Freeze (Intent(..), Scope(..))
-import Dhall.Import (Imported(..))
+import Dhall.Import (Imported(..), Depends(..))
 import Dhall.Parser (Src)
 import Dhall.Pretty (Ann, CharacterSet(..), annToAnsiStyle, layoutOpts)
 import Dhall.TypeCheck (DetailedTypeError(..), TypeError, X)
@@ -36,6 +37,7 @@
 import Options.Applicative (Parser, ParserInfo)
 import System.Exit (exitFailure)
 import System.IO (Handle)
+import Text.Dot ((.->.))
 
 import qualified Codec.CBOR.JSON
 import qualified Codec.CBOR.Read
@@ -70,6 +72,7 @@
 import qualified Paths_dhall as Meta
 import qualified System.Console.ANSI
 import qualified System.IO
+import qualified System.FilePath
 import qualified Text.Dot
 import qualified Data.Map
 
@@ -97,6 +100,7 @@
     | Lint { inplace :: Maybe FilePath }
     | Encode { file :: Maybe FilePath, json :: Bool }
     | Decode { file :: Maybe FilePath, json :: Bool }
+    | Text { file :: Maybe FilePath }
 
 data ResolveMode
     = Dot
@@ -183,6 +187,10 @@
             "decode"
             "Decode a Dhall expression from binary"
             (Decode <$> optional parseFile <*> parseJSONFlag)
+    <|> subcommand
+            "text"
+            "Render a Dhall expression that evaluates to a Text literal"
+            (Text <$> optional parseFile)
     <|> (Default <$> optional parseFile <*> parseAnnotate <*> parseAlpha)
   where
     argument =
@@ -299,7 +307,7 @@
           where
             file = case maybeFile of
                 Just "-" -> "."
-                Just f   -> f
+                Just f   -> System.FilePath.takeDirectory f
                 Nothing  -> "."
 
     let handle =
@@ -355,14 +363,7 @@
 
     handle $ case mode of
         Version -> do
-            let line₀ = "Haskell package version: "
-                    <>  Data.Text.pack (showVersion Meta.version)
-
-            let line₁ = "Standard version: "
-                    <>  Dhall.Binary.renderStandardVersion Dhall.Binary.defaultStandardVersion
-
-            Data.Text.IO.putStrLn line₀
-            Data.Text.IO.putStrLn line₁
+            putStrLn (showVersion Meta.version)
 
         Default {..} -> do
             expression <- getExpression file
@@ -388,13 +389,32 @@
         Resolve { resolveMode = Just Dot, ..} -> do
             expression <- getExpression file
 
-            (Dhall.Import.Types.Status { _dot}) <-
+            (Dhall.Import.Types.Status { _graph, _stack }) <-
                 State.execStateT (Dhall.Import.loadWith expression) (toStatus file)
 
-            putStr . ("strict " <>) . Text.Dot.showDot $
-                   Text.Dot.attribute ("rankdir", "LR") >>
-                   _dot
+            let (rootImport :| _) = _stack
+                imports = rootImport : map parent _graph ++ map child _graph
+                importIds = Data.Map.fromList (zip imports [Text.Dot.userNodeId i | i <- [0..]])
 
+            let dotNode (i, nodeId) =
+                    Text.Dot.userNode
+                        nodeId
+                        [ ("label", Data.Text.unpack $ pretty i)
+                        , ("shape", "box")
+                        , ("style", "rounded")
+                        ]
+
+            let dotEdge (Depends parent child) =
+                    case (Data.Map.lookup parent importIds, Data.Map.lookup child importIds) of
+                        (Just from, Just to) -> from .->. to
+                        _                    -> pure ()
+
+            let dot = do Text.Dot.attribute ("rankdir", "LR")
+                         mapM_ dotNode (Data.Map.assocs importIds)
+                         mapM_ dotEdge _graph
+
+            putStr . ("strict " <>) . Text.Dot.showDot $ dot
+
         Resolve { resolveMode = Just ListImmediateDependencies, ..} -> do
             expression <- getExpression file
 
@@ -411,7 +431,8 @@
             mapM_ print
                  .   fmap (   Pretty.pretty
                           .   Dhall.Core.importType
-                          .   Dhall.Core.importHashed )
+                          .   Dhall.Core.importHashed
+                          .   Dhall.Import.chainedImport )
                  .   Data.Map.keys
                  $   _cache
 
@@ -502,7 +523,7 @@
         Encode {..} -> do
             expression <- getExpression file
 
-            let term = Dhall.Binary.encode expression
+            let term = Dhall.Binary.encodeExpression expression
 
             let bytes = Codec.Serialise.serialise term
 
@@ -540,10 +561,32 @@
                         Dhall.Core.throws (Codec.Serialise.deserialiseOrFail bytes)
 
             expression <- Dhall.Core.throws (Dhall.Binary.decodeExpression term)
+                :: IO (Expr Src Import)
 
             let doc = Dhall.Pretty.prettyCharacterSet characterSet expression
 
             renderDoc System.IO.stdout doc
+
+        Text {..} -> do
+            expression <- getExpression file
+
+            resolvedExpression <- State.evalStateT (Dhall.Import.loadWith expression) (toStatus file)
+
+            _ <- Dhall.Core.throws (Dhall.TypeCheck.typeOf (Annot resolvedExpression Dhall.Core.Text))
+
+            let normalizedExpression = Dhall.Core.normalize resolvedExpression
+
+            case normalizedExpression of
+                Dhall.Core.TextLit (Dhall.Core.Chunks [] text) -> do
+                    Data.Text.IO.putStr text
+                _ -> do
+                    let invalidTypeExpected :: Expr X X
+                        invalidTypeExpected = Dhall.Core.Text
+
+                    let invalidTypeExpression :: Expr X X
+                        invalidTypeExpression = normalizedExpression
+
+                    Control.Exception.throwIO (Dhall.InvalidType {..})
 
 -- | Entry point for the @dhall@ executable
 main :: IO ()
diff --git a/src/Dhall/Map.hs b/src/Dhall/Map.hs
--- a/src/Dhall/Map.hs
+++ b/src/Dhall/Map.hs
@@ -1,5 +1,7 @@
 {-# LANGUAGE CPP                #-}
+{-# LANGUAGE DeriveAnyClass     #-}
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric      #-}
 {-# LANGUAGE DeriveTraversable  #-}
 {-# LANGUAGE RecordWildCards    #-}
 {-# LANGUAGE TypeFamilies       #-}
@@ -13,7 +15,12 @@
       -- * Construction
     , singleton
     , fromList
+    , fromListWithKey
 
+      -- * Constructing unordered 'Map's
+    , unorderedSingleton
+    , unorderedFromList
+
       -- * Sorting
     , sort
     , isSorted
@@ -25,16 +32,19 @@
       -- * Deletion/Update
     , delete
     , filter
+    , restrictKeys
     , mapMaybe
 
       -- * Query
     , lookup
     , member
     , uncons
+    , size
 
       -- * Combine
     , union
     , unionWith
+    , outerJoin
     , intersection
     , intersectionWith
     , difference
@@ -42,7 +52,7 @@
       -- * Traversals
     , mapWithKey
     , traverseWithKey
-    , traverseWithKey_
+    , unorderedTraverseWithKey
     , unorderedTraverseWithKey_
     , foldMapWithKey
 
@@ -50,15 +60,20 @@
     , toList
     , toMap
     , keys
+    , keysSet
+    , elems
     ) where
 
 import Control.Applicative ((<|>))
+import Control.DeepSeq (NFData)
 import Data.Data (Data)
-import Data.Foldable (traverse_)
 import Data.Semigroup
+import GHC.Generics (Generic)
+import Instances.TH.Lift ()
+import Language.Haskell.TH.Syntax (Lift)
 import Prelude hiding (filter, lookup)
 
-import qualified Data.Functor
+import qualified Data.List
 import qualified Data.Map
 import qualified Data.Set
 import qualified GHC.Exts
@@ -71,37 +86,61 @@
     This is done primarily to avoid a dependency on @insert-ordered-containers@
     and also to improve performance
 -}
-data Map k v = Map (Data.Map.Map k v) [k]
-    deriving (Data)
+data Map k v = Map (Data.Map.Map k v) (Keys k)
+    deriving (Data, Generic, NFData)
 
-instance (Eq k, Eq v) => Eq (Map k v) where
-  Map m1 ks == Map m2 ks' = m1 == m2 && ks == ks'
+instance (Data k, Data v, Lift k, Lift v, Ord k) => Lift (Map k v)
+
+data Keys a
+    = Sorted
+    | Original [a]
+    deriving (Data, Generic, NFData)
+
+instance (Data a, Lift a) => Lift (Keys a)
+
+instance (Ord k, Eq v) => Eq (Map k v) where
+  m1 == m2 =
+      Data.Map.size (toMap m1) == Data.Map.size (toMap m2)
+      && toList m1 == toList m2
   {-# INLINABLE (==) #-}
 
+{-|
+>>> fromList [("A",1),("B",2)] < fromList [("B",1),("A",0)]
+True
+-}
 instance (Ord k, Ord v) => Ord (Map k v) where
-  compare (Map mL ksL) (Map mR ksR) = compare mL mR <> compare ksL ksR
+  compare m1 m2 = compare (toList m1) (toList m2)
+  {-# INLINABLE compare #-}
 
 instance Functor (Map k) where
   fmap f (Map m ks) = Map (fmap f m) ks
   {-# INLINABLE fmap #-}
 
-instance Foldable (Map k) where
-  foldr f z (Map m _) = foldr f z m
+instance Ord k => Foldable (Map k) where
+  foldr f z (Map m Sorted) = foldr f z m
+  foldr f z m              = foldr f z (elems m)
   {-# INLINABLE foldr #-}
 
-  foldMap f (Map m _) = foldMap f m
-  {-# INLINABLE foldMap #-}
+  length m = size m
+  {-# INLINABLE length #-}
 
-instance Traversable (Map k) where
-  traverse f (Map m ks) = (\m' -> Map m' ks) <$> traverse f m
+instance Ord k => Traversable (Map k) where
+  traverse f m = traverseWithKey (\_ v -> f v) m
   {-# INLINABLE traverse #-}
 
+{-|
+prop> \x y z -> x <> (y <> z) == (x <> y) <> (z :: Map Int Int)
+-}
 instance Ord k => Data.Semigroup.Semigroup (Map k v) where
     (<>) = union
     {-# INLINABLE (<>) #-}
 
+{-|
+prop> \x -> x <> mempty == (x :: Map Int Int)
+prop> \x -> mempty <> x == (x :: Map Int Int)
+-}
 instance Ord k => Monoid (Map k v) where
-    mempty = Map Data.Map.empty []
+    mempty = Map Data.Map.empty (Original [])
     {-# INLINABLE mempty #-}
 
 #if !(MIN_VERSION_base(4,11,0))
@@ -132,28 +171,46 @@
   where
     m = Data.Map.singleton k v
 
-    ks = pure k
+    ks = Original [k]
 {-# INLINABLE singleton #-}
 
 {-| Create a `Map` from a list of key-value pairs
 
-> fromList empty = mempty
->
-> fromList (x <|> y) = fromList x <> fromList y
-
 >>> fromList [("B",1),("A",2)]  -- The map preserves order
 fromList [("B",1),("A",2)]
 >>> fromList [("A",1),("A",2)]  -- For duplicates, later values take precedence
 fromList [("A",2)]
+
+Note that this handling of duplicates means that 'fromList' is /not/ a monoid
+homomorphism:
+
+>>> fromList [(1, True)] <> fromList [(1, False)]
+fromList [(1,True)]
+>>> fromList ([(1, True)] <> [(1, False)])
+fromList [(1,False)]
+
 -}
 fromList :: Ord k => [(k, v)] -> Map k v
 fromList kvs = Map m ks
   where
     m = Data.Map.fromList kvs
 
-    ks = nubOrd (map fst kvs)
+    ks = Original (nubOrd (map fst kvs))
 {-# INLINABLE fromList #-}
 
+{-| Create a `Map` from a list of key-value pairs with a combining function.
+
+>>> fromListWithKey (\k v1 v2 -> k ++ v1 ++ v2) [("B","v1"),("A","v2"),("B","v3")]
+fromList [("B","Bv3v1"),("A","v2")]
+-}
+fromListWithKey :: Ord k => (k -> v -> v -> v) -> [(k, v)] -> Map k v
+fromListWithKey f kvs = Map m ks
+  where
+    m = Data.Map.fromListWithKey f kvs
+
+    ks = Original (nubOrd (map fst kvs))
+{-# INLINABLE fromListWithKey #-}
+
 {-| Remove duplicates from a  list
 
 >>> nubOrd [1,2,3]
@@ -170,6 +227,36 @@
         | otherwise             = k : go (Data.Set.insert k set) ks
 {-# INLINABLE nubOrd #-}
 
+{-| Create a `Map` from a single key-value pair.
+
+    Any further operations on this map will not retain the order of the keys.
+
+>>> unorderedSingleton "A" 1
+fromList [("A",1)]
+-}
+unorderedSingleton :: k -> v -> Map k v
+unorderedSingleton k v = Map m Sorted
+  where
+    m = Data.Map.singleton k v
+{-# INLINABLE unorderedSingleton #-}
+
+{-| Create a `Map` from a list of key-value pairs
+
+    Any further operations on this map will not retain the order of the keys.
+
+>>> unorderedFromList []
+fromList []
+>>> unorderedFromList [("B",1),("A",2)]  -- The map /doesn't/ preserve order
+fromList [("A",2),("B",1)]
+>>> unorderedFromList [("A",1),("A",2)]  -- For duplicates, later values take precedence
+fromList [("A",2)]
+-}
+unorderedFromList :: Ord k => [(k, v)] -> Map k v
+unorderedFromList kvs = Map m Sorted
+  where
+    m = Data.Map.fromList kvs
+{-# INLINABLE unorderedFromList #-}
+
 {-| Sort the keys of a `Map`, forgetting the original ordering
 
 > sort (sort x) = sort x
@@ -177,10 +264,8 @@
 >>> sort (fromList [("B",1),("A",2)])
 fromList [("A",2),("B",1)]
 -}
-sort :: Ord k => Map k v -> Map k v
-sort (Map m _) = Map m ks
-  where
-    ks = Data.Map.keys m
+sort :: Map k v -> Map k v
+sort (Map m _) = Map m Sorted
 {-# INLINABLE sort #-}
 
 {-| Check if the keys of a `Map` are already sorted
@@ -193,7 +278,8 @@
 True
 -}
 isSorted :: Eq k => Map k v -> Bool
-isSorted (Map m k) = Data.Map.keys m == k
+isSorted (Map _ Sorted)        = True
+isSorted (Map m (Original ks)) = Data.Map.keys m == ks -- Or shortcut to False here?
 {-# INLINABLE isSorted #-}
 
 {-| Insert a key-value pair into a `Map`, overriding any previous value stored
@@ -207,12 +293,13 @@
 fromList [("C",1),("A",3)]
 -}
 insert :: Ord k => k -> v -> Map k v -> Map k v
-insert k v (Map m ks) = Map m' ks'
+insert k v (Map m Sorted)        = Map (Data.Map.insert k v m) Sorted
+insert k v (Map m (Original ks)) = Map m' (Original ks')
   where
-    m' = Data.Map.insert k v m
+    (mayOldV, m') = Data.Map.insertLookupWithKey (\_k new _old -> new) k v m
 
-    ks' | elem k ks = ks
-        | otherwise = k : ks
+    ks' | Just _ <- mayOldV = ks
+        | otherwise         = k : ks
 {-# INLINABLE insert #-}
 
 {-| Insert a key-value pair into a `Map`, using the supplied function to combine
@@ -224,12 +311,13 @@
 fromList [("C",3),("A",3)]
 -}
 insertWith :: Ord k => (v -> v -> v) -> k -> v -> Map k v -> Map k v
-insertWith f k v (Map m ks) = Map m' ks'
+insertWith f k v (Map m Sorted)        = Map (Data.Map.insertWith f k v m) Sorted
+insertWith f k v (Map m (Original ks)) = Map m' (Original ks')
   where
-    m' = Data.Map.insertWith f k v m
+    (mayOldV, m') = Data.Map.insertLookupWithKey (\_k new old -> f new old) k v m
 
-    ks' | elem k ks = ks
-        | otherwise = k : ks
+    ks' | Just _ <- mayOldV = ks
+        | otherwise         = k : ks
 {-# INLINABLE insertWith #-}
 
 {-| Delete a key from a `Map` if present, otherwise return the original `Map`
@@ -244,7 +332,9 @@
   where
     m' = Data.Map.delete k m
 
-    ks' = Prelude.filter (k /=) ks
+    ks' = case ks of
+        Sorted        -> Sorted
+        Original ks'' -> Original (Data.List.delete k ks'')
 {-# INLINABLE delete #-}
 
 {-| Keep all values that satisfy the given predicate
@@ -259,11 +349,26 @@
   where
     m' = Data.Map.filter predicate m
 
-    set = Data.Map.keysSet m'
-
-    ks' = Prelude.filter (\k -> Data.Set.member k set) ks
+    ks' = filterKeys (\k -> Data.Map.member k m') ks
 {-# INLINABLE filter #-}
 
+{-| Restrict a 'Map' to only those keys found in a @"Data.Set".'Set'@.
+
+>>> restrictKeys (fromList [("A",1),("B",2)]) (Data.Set.fromList ["A"])
+fromList [("A",1)]
+-}
+restrictKeys :: Ord k => Map k a -> Data.Set.Set k -> Map k a
+restrictKeys (Map m ks) s = Map m' ks'
+  where
+#if MIN_VERSION_containers(0,5,8)
+    m' = Data.Map.restrictKeys m s
+#else
+    m' = Data.Map.filterWithKey (\k _ -> Data.Set.member k s) m
+#endif
+
+    ks' = filterKeys (\k -> Data.Set.member k s) ks
+{-# INLINABLE restrictKeys #-}
+
 {-| Transform all values in a `Map` using the supplied function, deleting the
     key if the function returns `Nothing`
 
@@ -275,9 +380,7 @@
   where
     m' = Data.Map.mapMaybe f m
 
-    set = Data.Map.keysSet m'
-
-    ks' = Prelude.filter (\k -> Data.Set.member k set) ks
+    ks' = filterKeys (\k -> Data.Map.member k m') ks
 {-# INLINABLE mapMaybe #-}
 
 {-| Retrieve a key from a `Map`
@@ -308,8 +411,12 @@
 Nothing
 -}
 uncons :: Ord k => Map k v -> Maybe (k, v, Map k v)
-uncons (Map _ [])     = Nothing
-uncons (Map m (k:ks)) = Just (k, m Data.Map.! k, Map (Data.Map.delete k m) ks)
+uncons (Map _ (Original []))     = Nothing
+uncons (Map m (Original (k:ks))) =
+    Just (k, m Data.Map.! k, Map (Data.Map.delete k m) (Original ks))
+uncons (Map m Sorted)
+  | Just ((k, v), m') <- Data.Map.minViewWithKey m = Just (k, v, Map m' Sorted)
+  | otherwise                                      = Nothing
 {-# INLINABLE uncons #-}
 
 {-| Check if a key belongs to a `Map`
@@ -327,6 +434,14 @@
 member k (Map m _) = Data.Map.member k m
 {-# INLINABLE member #-}
 
+{-|
+>>> size (fromList [("A",1)])
+1
+-}
+size :: Map k v -> Int
+size (Map m _) = Data.Map.size m
+{-# INLINABLE size #-}
+
 {-| Combine two `Map`s, preferring keys from the first `Map`
 
 > union = unionWith (\v _ -> v)
@@ -341,9 +456,10 @@
   where
     m = Data.Map.union mL mR
 
-    setL = Data.Map.keysSet mL
-
-    ks = ksL <|> Prelude.filter (\k -> Data.Set.notMember k setL) ksR
+    ks = case (ksL, ksR) of
+        (Original l, Original r) -> Original $
+            l <|> Prelude.filter (\k -> Data.Map.notMember k mL) r
+        _                        -> Sorted
 {-# INLINABLE union #-}
 
 {-| Combine two `Map`s using a combining function for colliding keys
@@ -358,11 +474,42 @@
   where
     m = Data.Map.unionWith combine mL mR
 
-    setL = Data.Map.keysSet mL
-
-    ks = ksL <|> Prelude.filter (\k -> Data.Set.notMember k setL) ksR
+    ks = case (ksL, ksR) of
+        (Original l, Original r) -> Original $
+            l <|> Prelude.filter (\k -> Data.Map.notMember k mL) r
+        _                        -> Sorted
 {-# INLINABLE unionWith #-}
 
+{-| A generalised 'unionWith'.
+
+>>> outerJoin Left Left (\k a b -> Right (k, a, b)) (fromList [("A",1),("B",2)]) (singleton "A" 3)
+fromList [("A",Right ("A",1,3)),("B",Left 2)]
+
+This function is much inspired by the "Data.Semialign.Semialign" class.
+-}
+outerJoin
+    :: Ord k
+    => (a -> c)
+    -> (b -> c)
+    -> (k -> a -> b -> c)
+    -> Map k a
+    -> Map k b
+    -> Map k c
+outerJoin fa fb fab (Map ma ksA) (Map mb ksB) = Map m ks
+  where
+    m = Data.Map.mergeWithKey
+            (\k a b -> Just (fab k a b))
+            (fmap fa)
+            (fmap fb)
+            ma
+            mb
+
+    ks = case (ksA, ksB) of
+        (Original l, Original r) -> Original $
+            l <|> Prelude.filter (\k -> Data.Map.notMember k ma) r
+        _                        -> Sorted
+{-# INLINABLE outerJoin #-}
+
 {-| Combine two `Map` on their shared keys, keeping the value from the first
     `Map`
 
@@ -376,10 +523,8 @@
   where
     m = Data.Map.intersection mL mR
 
-    setL = Data.Map.keysSet mL
-    setR = Data.Map.keysSet mR
-    set  = Data.Set.intersection setL setR
-    ks   = Prelude.filter (\k -> Data.Set.member k set) ksL
+    -- Or forget order unless both maps are ordered?!
+    ks = filterKeys (\k -> Data.Map.member k m) ksL
 {-# INLINABLE intersection #-}
 
 {-| Combine two `Map`s on their shared keys, using the supplied function to
@@ -393,10 +538,8 @@
   where
     m = Data.Map.intersectionWith combine mL mR
 
-    setL = Data.Map.keysSet mL
-    setR = Data.Map.keysSet mR
-    set  = Data.Set.intersection setL setR
-    ks   = Prelude.filter (\k -> Data.Set.member k set) ksL
+    -- Or forget order unless both maps are ordered?!
+    ks = filterKeys (\k -> Data.Map.member k m) ksL
 {-# INLINABLE intersectionWith #-}
 
 {-| Compute the difference of two `Map`s by subtracting all keys from the
@@ -410,9 +553,7 @@
   where
     m = Data.Map.difference mL mR
 
-    setR = Data.Map.keysSet mR
-
-    ks = Prelude.filter (\k -> Data.Set.notMember k setR) ksL
+    ks = filterKeys (\k -> Data.Map.notMember k mR) ksL
 {-# INLINABLE difference #-}
 
 {-| Fold all of the key-value pairs in a `Map`, in their original order
@@ -421,7 +562,8 @@
 ("BA",[1,2])
 -}
 foldMapWithKey :: (Monoid m, Ord k) => (k -> a -> m) -> Map k a -> m
-foldMapWithKey f m = foldMap (uncurry f) (toList m)
+foldMapWithKey f (Map m Sorted) = Data.Map.foldMapWithKey f m
+foldMapWithKey f m              = foldMap (uncurry f) (toList m)
 {-# INLINABLE foldMapWithKey #-}
 
 {-| Transform the values of a `Map` using their corresponding key
@@ -450,33 +592,33 @@
 -}
 traverseWithKey
     :: Ord k => Applicative f => (k -> a -> f b) -> Map k a -> f (Map k b)
+traverseWithKey f (Map m Sorted) =
+    fmap (\m' -> Map m' Sorted) (Data.Map.traverseWithKey f m)
 traverseWithKey f m =
     fmap fromList (traverse f' (toList m))
   where
     f' (k, a) = fmap ((,) k) (f k a)
 {-# INLINABLE traverseWithKey #-}
 
-{-| Traverse all of the key-value pairs in a `Map`, in their original order
-    where the result of the computation can be forgotten.
-
->>> traverseWithKey_ (\k v -> print (k, v)) (fromList [("B",1),("A",2)])
-("B",1)
-("A",2)
+{-| Same as `traverseWithKey`, except that the order of effects is not
+    necessarily the same as the order of the keys
 -}
-traverseWithKey_
-    :: Ord k => Applicative f => (k -> a -> f ()) -> Map k a -> f ()
-traverseWithKey_ f m = Data.Functor.void (traverseWithKey f m)
-{-# INLINABLE traverseWithKey_ #-}
+unorderedTraverseWithKey
+    :: Ord k => Applicative f => (k -> a -> f b) -> Map k a -> f (Map k b)
+unorderedTraverseWithKey f (Map m ks) =
+    fmap (\m' -> Map m' ks) (Data.Map.traverseWithKey f m)
+{-# INLINABLE unorderedTraverseWithKey #-}
 
-{-| Travese all of the key-value pairs in a 'Map', not preserving their
+{-| Traverse all of the key-value pairs in a 'Map', not preserving their
     original order, where the result of the computation can be forgotten.
 
-    Note that this is an optimisation over 'traverseWithKey_' since we do
-    not care in what order we traverse the pairs.
+    Note that this is a strict traversal, fully traversing the map even
+    when the Applicative is lazy in the remaining elements.
 -}
 unorderedTraverseWithKey_
     :: Ord k => Applicative f => (k -> a -> f ()) -> Map k a -> f ()
-unorderedTraverseWithKey_ f = Data.Functor.void . traverse_ (uncurry f) . toList
+unorderedTraverseWithKey_ f (Map m _) =
+    Data.Map.foldlWithKey' (\acc k v -> acc *> f k v) (pure ()) m
 {-# INLINABLE unorderedTraverseWithKey_ #-}
 
 {-| Convert a `Map` to a list of key-value pairs in the original order of keys
@@ -485,7 +627,8 @@
 [("B",1),("A",2)]
 -}
 toList :: Ord k => Map k v -> [(k, v)]
-toList (Map m ks) = fmap (\k -> (k, m Data.Map.! k)) ks
+toList (Map m Sorted)        = Data.Map.toList m
+toList (Map m (Original ks)) = fmap (\k -> (k, m Data.Map.! k)) ks
 {-# INLINABLE toList #-}
 
 {-| Convert a @"Dhall.Map".`Map`@ to a @"Data.Map".`Data.Map.Map`@
@@ -503,5 +646,38 @@
 ["B","A"]
 -}
 keys :: Map k v -> [k]
-keys (Map _ ks) = ks
+keys (Map m Sorted)        = Data.Map.keys m
+keys (Map _ (Original ks)) = ks
 {-# INLINABLE keys #-}
+
+{-| Return the values from a `Map` in their original order.
+
+>>> elems (fromList [("B",1),("A",2)])
+[1,2]
+-}
+elems :: Ord k => Map k v -> [v]
+elems (Map m Sorted)        = Data.Map.elems m
+elems (Map m (Original ks)) = fmap (\k -> m Data.Map.! k) ks
+{-# INLINABLE elems #-}
+
+{-| Return the @"Data.Set".'Set'@ of the keys
+
+>>> keysSet (fromList [("B",1),("A",2)])
+fromList ["A","B"]
+-}
+keysSet :: Map k v -> Data.Set.Set k
+keysSet (Map m _) = Data.Map.keysSet m
+{-# INLINABLE keysSet #-}
+
+filterKeys :: (a -> Bool) -> Keys a -> Keys a
+filterKeys _ Sorted        = Sorted
+filterKeys f (Original ks) = Original (Prelude.filter f ks)
+{-# INLINABLE filterKeys #-}
+
+{- $setup
+>>> import Test.QuickCheck (Arbitrary(..), oneof)
+>>> :{
+  instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map k v) where
+    arbitrary = oneof [fromList <$> arbitrary, unorderedFromList <$> arbitrary]
+:}
+-}
diff --git a/src/Dhall/Parser/Combinators.hs b/src/Dhall/Parser/Combinators.hs
--- a/src/Dhall/Parser/Combinators.hs
+++ b/src/Dhall/Parser/Combinators.hs
@@ -9,7 +9,6 @@
 import           Control.Exception          (Exception)
 import           Control.Monad              (MonadPlus (..))
 import           Data.Semigroup             (Semigroup (..))
-import           Data.Sequence              (ViewL (..))
 import           Data.String                (IsString (..))
 import           Data.Text                  (Text)
 import           Data.Text.Prettyprint.Doc  (Pretty (..))
@@ -23,7 +22,6 @@
 
 import qualified Control.Monad.Fail
 import qualified Data.Char
-import qualified Data.Sequence
 import qualified Data.Set
 import qualified Data.Text
 import qualified Data.Text.Prettyprint.Doc               as Pretty
@@ -256,19 +254,9 @@
         else go (Dhall.Set.append x found) xs
 
 toMap :: [(Text, a)] -> Parser (Map Text a)
-toMap kvs = do
-    let adapt (k, v) = (k, pure v)
-    let m = fromListWith (<|>) (fmap adapt kvs)
-    let action k vs = case Data.Sequence.viewl vs of
-            EmptyL  -> empty
-            v :< vs' ->
-                if null vs'
-                then pure v
-                else
-                    Text.Parser.Combinators.unexpected
-                        ("duplicate field: " ++ Data.Text.unpack k)
-    Dhall.Map.traverseWithKey action m
+toMap kvs = Dhall.Map.unorderedTraverseWithKey (\_k v -> v) m
   where
-    fromListWith combine = foldr cons mempty
-      where
-        cons (k, v) = Dhall.Map.insertWith combine k v
+    m = Dhall.Map.fromListWithKey err (map (\(k, v) -> (k, pure v)) kvs)
+
+    err k _v1 _v2 = Text.Parser.Combinators.unexpected
+                        ("duplicate field: " ++ Data.Text.unpack k)
diff --git a/src/Dhall/Parser/Expression.hs b/src/Dhall/Parser/Expression.hs
--- a/src/Dhall/Parser/Expression.hs
+++ b/src/Dhall/Parser/Expression.hs
@@ -19,18 +19,20 @@
 import Prelude hiding (const, pi)
 import Text.Parser.Combinators (choice, try, (<?>))
 
+import qualified Control.Monad
 import qualified Crypto.Hash
 import qualified Data.ByteArray.Encoding
 import qualified Data.ByteString
-import qualified Data.Char
+import qualified Data.Char               as Char
 import qualified Data.Foldable
+import qualified Data.List
 import qualified Data.List.NonEmpty
 import qualified Data.Sequence
 import qualified Data.Text
 import qualified Data.Text.Encoding
 import qualified Text.Megaparsec
 #if !MIN_VERSION_megaparsec(7, 0, 0)
-import qualified Text.Megaparsec.Char as Text.Megaparsec
+import qualified Text.Megaparsec.Char    as Text.Megaparsec
 #endif
 import qualified Text.Parser.Char
 
@@ -75,13 +77,24 @@
         Note src₁ _ | laxSrcEq src₀ src₁ -> return e
         _                                -> return (Note src₀ e)
 
-shallowDenote :: Expr s a -> Expr s a
-shallowDenote (Note _ e) = shallowDenote e
-shallowDenote         e  = e
-
 completeExpression :: Parser a -> Parser (Expr Src a)
 completeExpression embedded = completeExpression_
   where
+    Parsers {..} = parsers embedded
+
+importExpression :: Parser a -> Parser (Expr Src a)
+importExpression embedded = importExpression_
+  where
+    Parsers {..} = parsers embedded
+
+data Parsers a = Parsers
+    { completeExpression_ :: Parser (Expr Src a)
+    , importExpression_   :: Parser (Expr Src a)
+    }
+
+parsers :: Parser a -> Parsers a
+parsers embedded = Parsers {..}
+  where
     completeExpression_ = do
         whitespace
         expression
@@ -164,20 +177,13 @@
 
                     b <- expression
 
-                    case (shallowDenote a, shallowDenote b) of
-                        (ListLit _ xs, App f c) ->
-                            case shallowDenote f of
-                                List     -> case xs of
-                                    [] -> return (ListLit (Just c) xs)
-                                    _  -> return (Annot a b)
-                                Optional -> case xs of
-                                    [x] -> return (OptionalLit c (Just x))
-                                    []  -> return (OptionalLit c Nothing)
-                                    _   -> return (Annot a b)
-                                _ ->
-                                    return (Annot a b)
-                        (Merge c d _, e) ->
-                            return (Merge c d (Just e))
+                    case shallowDenote a of
+                        ListLit _ [] ->
+                            return (ListLit (Just b) [])
+                        Merge c d _ ->
+                            return (Merge c d (Just b))
+                        ToMap c _ ->
+                            return (ToMap c (Just b))
                         _ -> return (Annot a b)
 
             alternative4A <|> alternative4B <|> pure a
@@ -228,8 +234,8 @@
     applicationExpression = do
             f <-    (do _Some; return Some)
                 <|> return id
-            a <- noted importExpression
-            b <- Text.Megaparsec.many (noted importExpression)
+            a <- noted importExpression_
+            b <- Text.Megaparsec.many (noted importExpression_)
             return (foldl app (f a) b)
           where
             app nL@(Note (Src before _ bytesL) _) nR@(Note (Src _ after bytesR) _) =
@@ -237,7 +243,7 @@
             app nL nR =
                 App nL nR
 
-    importExpression = noted (choice [ alternative0, alternative1 ])
+    importExpression_ = noted (choice [ alternative0, alternative1 ])
           where
             alternative0 = do
                 a <- embedded
@@ -273,6 +279,7 @@
                     , alternative05
                     , alternative06
                     , alternative07
+                    , alternative08
                     , alternative37
                     , alternative09
 
@@ -315,10 +322,15 @@
 
             alternative07 = do
                 _merge
-                a <- importExpression
-                b <- importExpression
+                a <- importExpression_
+                b <- importExpression_ <?> "second argument to ❰merge❱"
                 return (Merge a b Nothing)
 
+            alternative08 = do
+                _toMap
+                a <- importExpression_
+                return (ToMap a Nothing)
+
             alternative09 = do
                 a <- try doubleInfinity
                 return (DoubleLit a)
@@ -471,13 +483,29 @@
 
                 unicode = do
                     _  <- Text.Parser.Char.char 'u';
-                    n0 <- hexNumber
-                    n1 <- hexNumber
-                    n2 <- hexNumber
-                    n3 <- hexNumber
-                    let n = ((n0 * 16 + n1) * 16 + n2) * 16 + n3
-                    return (Data.Char.chr n)
 
+                    let toNumber = Data.List.foldl' (\x y -> x * 16 + y) 0
+
+                    let fourCharacterEscapeSequence =
+                            fmap toNumber (Control.Monad.replicateM 4 hexNumber)
+
+                    let bracedEscapeSequence = do
+                            _  <- Text.Parser.Char.char '{'
+                            ns <- some hexNumber
+
+                            let number = toNumber ns
+
+                            Control.Monad.guard (number <= 0x10FFFF && validCodepoint (Char.chr number))
+                                <|> fail "Invalid Unicode code point"
+
+                            _  <- Text.Parser.Char.char '}'
+
+                            return (toNumber ns)
+
+                    n <- bracedEscapeSequence <|> fourCharacterEscapeSequence
+
+                    return (Char.chr n)
+
     doubleQuotedLiteral = do
             _      <- Text.Parser.Char.char '"'
             chunks <- Text.Megaparsec.many doubleQuotedChunk
@@ -703,7 +731,7 @@
     whitespace
     headers <- optional (do
         _using
-        (completeExpression import_ <|> (_openParens *> completeExpression import_ <* _closeParens)) )
+        importExpression import_ )
     return (Remote (url { headers }))
 
 missing :: Parser ImportType
@@ -720,23 +748,24 @@
 
     choice [ local, http, env, missing ]
 
+importHash_ :: Parser (Crypto.Hash.Digest Crypto.Hash.SHA256)
+importHash_ = do
+    _ <- Text.Parser.Char.text "sha256:"
+    text <- count 64 (satisfy hexdig <?> "hex digit")
+    whitespace
+    let strictBytes16 = Data.Text.Encoding.encodeUtf8 text
+    strictBytes <- case Data.ByteArray.Encoding.convertFromBase Base16 strictBytes16 of
+        Left  string      -> fail string
+        Right strictBytes -> return (strictBytes :: Data.ByteString.ByteString)
+    case Crypto.Hash.digestFromByteString strictBytes of
+      Nothing -> fail "Invalid sha256 hash"
+      Just h  -> pure h
+
 importHashed_ :: Parser ImportHashed
 importHashed_ = do
     importType <- importType_
     hash       <- optional importHash_
     return (ImportHashed {..})
-  where
-    importHash_ = do
-        _ <- Text.Parser.Char.text "sha256:"
-        text <- count 64 (satisfy hexdig <?> "hex digit")
-        whitespace
-        let strictBytes16 = Data.Text.Encoding.encodeUtf8 text
-        strictBytes <- case Data.ByteArray.Encoding.convertFromBase Base16 strictBytes16 of
-            Left  string      -> fail string
-            Right strictBytes -> return (strictBytes :: Data.ByteString.ByteString)
-        case Crypto.Hash.digestFromByteString strictBytes of
-          Nothing -> fail "Invalid sha256 hash"
-          Just h  -> pure h
 
 import_ :: Parser Import
 import_ = (do
@@ -745,9 +774,8 @@
     return (Import {..}) ) <?> "import"
   where
     alternative = do
-        _as
-        _Text
-        return RawText
+      _as
+      (_Text >> pure RawText) <|> (_Location >> pure Location)
 
 -- | Similar to `Dhall.Core.renderChunks` except that this doesn't bother to
 -- render interpolated expressions to avoid a `Buildable a` constraint.  The
diff --git a/src/Dhall/Parser/Token.hs b/src/Dhall/Parser/Token.hs
--- a/src/Dhall/Parser/Token.hs
+++ b/src/Dhall/Parser/Token.hs
@@ -3,6 +3,7 @@
 -- | Parse Dhall tokens. Even though we don't have a tokenizer per-se this
 ---  module is useful for keeping some small parsing utilities.
 module Dhall.Parser.Token (
+    validCodepoint,
     whitespace,
     bashEnvironmentVariable,
     posixEnvironmentVariable,
@@ -28,7 +29,7 @@
     _as,
     _using,
     _merge,
-    _constructors,
+    _toMap,
     _Some,
     _None,
     _NaturalFold,
@@ -63,6 +64,7 @@
     _Type,
     _Kind,
     _Sort,
+    _Location,
     _equal,
     _or,
     _plus,
@@ -107,7 +109,7 @@
 import Text.Parser.Combinators (choice, try, (<?>))
 
 import qualified Control.Monad
-import qualified Data.Char
+import qualified Data.Char                  as Char
 import qualified Data.HashSet
 import qualified Data.List.NonEmpty
 import qualified Data.Text
@@ -123,6 +125,11 @@
 
 import qualified Text.Parser.Token
 
+validCodepoint :: Char -> Bool
+validCodepoint c =
+    not (category == Char.Surrogate || category == Char.NotAssigned)
+  where
+    category = Char.generalCategory c
 
 whitespace :: Parser ()
 whitespace = Text.Parser.Combinators.skipMany whitespaceChunk
@@ -136,6 +143,9 @@
 digit :: Char -> Bool
 digit c = '\x30' <= c && c <= '\x39'
 
+alphaNum :: Char -> Bool
+alphaNum c = alpha c || digit c
+
 hexdig :: Char -> Bool
 hexdig c =
         ('0' <= c && c <= '9')
@@ -203,19 +213,19 @@
   where
     hexDigit = do
         c <- Text.Parser.Char.satisfy predicate
-        return (Data.Char.ord c - Data.Char.ord '0')
+        return (Char.ord c - Char.ord '0')
       where
         predicate c = '0' <= c && c <= '9'
 
     hexUpper = do
         c <- Text.Parser.Char.satisfy predicate
-        return (10 + Data.Char.ord c - Data.Char.ord 'A')
+        return (10 + Char.ord c - Char.ord 'A')
       where
         predicate c = 'A' <= c && c <= 'F'
 
     hexLower = do
         c <- Text.Parser.Char.satisfy predicate
-        return (10 + Data.Char.ord c - Data.Char.ord 'a')
+        return (10 + Char.ord c - Char.ord 'a')
       where
         predicate c = 'a' <= c && c <= 'f'
 
@@ -254,7 +264,7 @@
         predicate c =
                 '\x20' <= c && c <= '\x10FFFF' && c /= '-' && c /= '{'
             ||  c == '\n'
-            || c == '\t'
+            ||  c == '\t'
 
     character = void (Text.Parser.Char.satisfy predicate)
       where
@@ -281,7 +291,7 @@
   where
     headCharacter c = alpha c || c == '_'
 
-    tailCharacter c = alpha c || digit c || c == '_' || c == '-' || c == '/'
+    tailCharacter c = alphaNum c || c == '_' || c == '-' || c == '/'
 
 backtickLabel :: Parser Text
 backtickLabel = do
@@ -326,7 +336,7 @@
   where
     predicate0 c = alpha c || c == '_'
 
-    predicate1 c = alpha c || digit c || c == '_'
+    predicate1 c = alphaNum c || c == '_'
 
 posixEnvironmentVariable :: Parser Text
 posixEnvironmentVariable = plus posixEnvironmentVariableCharacter
@@ -370,13 +380,15 @@
 pathComponent componentType = do
     _ <- "/" :: Parser Text
 
-    let pathData = do
-            text <- Text.Megaparsec.takeWhile1P Nothing Dhall.Core.pathCharacter
-
+    let pathData =
             case componentType of
-                FileComponent -> return text
-                URLComponent  -> return (URI.Encode.decodeText text)
+                FileComponent -> do
+                    Text.Megaparsec.takeWhile1P Nothing Dhall.Core.pathCharacter
+                URLComponent -> do
+                    text <- star pchar
 
+                    return (URI.Encode.decodeText text)
+
     let quotedPathData = do
             _    <- Text.Parser.Char.char '"'
             text <- Text.Megaparsec.takeWhile1P Nothing quotedPathCharacter
@@ -384,12 +396,17 @@
 
             return text
 
-    pathData <|> quotedPathData
+    quotedPathData <|> pathData
 
 file_ :: ComponentType -> Parser File
 file_ componentType = do
-    path <- Data.List.NonEmpty.some1 (pathComponent componentType)
+    let emptyPath =
+            case componentType of
+                URLComponent  -> pure (pure "")
+                FileComponent -> empty
 
+    path <- Data.List.NonEmpty.some1 (pathComponent componentType) <|> emptyPath
+
     let directory = Directory (reverse (Data.List.NonEmpty.init path))
     let file      = Data.List.NonEmpty.last path
 
@@ -421,7 +438,7 @@
     predicate c = unreserved c || subDelims c || c == ':'
 
 host :: Parser Text
-host = choice [ ipLiteral, ipV4Address, regName ]
+host = choice [ ipLiteral, try ipV4Address, domain ]
 
 port :: Parser Text
 port = star (satisfy digit)
@@ -517,10 +534,13 @@
       where
         predicate c = '\x30' <= c && c <= '\x35'
 
-regName :: Parser Text
-regName = star (satisfy predicate <|> pctEncoded)
+domain :: Parser Text
+domain = domainLabel <> star ("." <> domainLabel ) <> option "."
+
+domainLabel :: Parser Text
+domainLabel = plus alphaNum_ <> star (plus "-" <> plus alphaNum_)
   where
-    predicate c = unreserved c || subDelims c
+    alphaNum_ = satisfy alphaNum
 
 pchar :: Parser Text
 pchar = satisfy predicate <|> pctEncoded
@@ -540,7 +560,7 @@
 
 unreserved :: Char -> Bool
 unreserved c =
-    alpha c || digit c || c == '-' || c == '.' || c == '_' || c == '~'
+    alphaNum c || c == '-' || c == '.' || c == '_' || c == '~'
 
 reserved :: Data.Text.Text -> Parser ()
 reserved x = do _ <- Text.Parser.Char.text x; whitespace
@@ -575,8 +595,8 @@
 _merge :: Parser ()
 _merge = keyword "merge"
 
-_constructors :: Parser ()
-_constructors = keyword "constructors"
+_toMap :: Parser ()
+_toMap = keyword "toMap"
 
 _Some :: Parser ()
 _Some = keyword "Some"
@@ -682,6 +702,9 @@
 
 _Sort :: Parser ()
 _Sort = reserved "Sort"
+
+_Location :: Parser ()
+_Location = reserved "Location"
 
 _equal :: Parser ()
 _equal = reservedChar '='
diff --git a/src/Dhall/Pretty/Internal.hs b/src/Dhall/Pretty/Internal.hs
--- a/src/Dhall/Pretty/Internal.hs
+++ b/src/Dhall/Pretty/Internal.hs
@@ -24,6 +24,7 @@
     , prettyLabels
     , prettyNatural
     , prettyNumber
+    , prettyInt
     , prettyDouble
     , prettyToStrictText
     , prettyToString
@@ -327,6 +328,9 @@
 prettyNumber :: Integer -> Doc Ann
 prettyNumber = literal . Pretty.pretty
 
+prettyInt :: Int -> Doc Ann
+prettyInt = literal . Pretty.pretty
+
 prettyNatural :: Natural -> Doc Ann
 prettyNatural = literal . Pretty.pretty
 
@@ -340,10 +344,11 @@
 
 prettyVar :: Var -> Doc Ann
 prettyVar (V x 0) = label (Pretty.unAnnotate (prettyLabel x))
-prettyVar (V x n) = label (Pretty.unAnnotate (prettyLabel x <> "@" <> prettyNumber n))
+prettyVar (V x n) = label (Pretty.unAnnotate (prettyLabel x <> "@" <> prettyInt n))
 
 prettyCharacterSet :: Pretty a => CharacterSet -> Expr s a -> Doc Ann
-prettyCharacterSet characterSet = prettyExpression
+prettyCharacterSet characterSet expression =
+    Pretty.group (prettyExpression expression)
   where
     prettyExpression a0@(Lam _ _ _) =
         arrows characterSet (fmap duplicate (docs a0))
@@ -523,6 +528,35 @@
             <>  prettyImportExpression a
             <>  " "
             <>  prettyImportExpression b
+    prettyAnnotatedExpression (ToMap a (Just b)) =
+        Pretty.group (Pretty.flatAlt long short)
+      where
+        long =
+            Pretty.align
+                (   keyword "toMap"
+                <>  Pretty.hardline
+                <>  prettyImportExpression a
+                <>  Pretty.hardline
+                <>  colon <> space
+                <>  prettyApplicationExpression b
+                )
+
+        short = keyword "toMap" <> space
+            <>  prettyImportExpression a
+            <>  space <> colon <> space
+            <>  prettyApplicationExpression b
+    prettyAnnotatedExpression (ToMap a Nothing) =
+        Pretty.group (Pretty.flatAlt long short)
+      where
+        long =
+            Pretty.align
+                (   keyword "toMap"
+                <>  Pretty.hardline
+                <>  prettyImportExpression a
+                )
+
+        short = keyword "toMap" <> space
+            <>  prettyImportExpression a
     prettyAnnotatedExpression a0@(Annot _ _) =
         enclose'
             ""
@@ -537,11 +571,7 @@
     prettyAnnotatedExpression (ListLit (Just a) b) =
             list (map prettyExpression (Data.Foldable.toList b))
         <>  " : "
-        <>  prettyApplicationExpression (App List a)
-    prettyAnnotatedExpression (OptionalLit a b) =
-            list (map prettyExpression (Data.Foldable.toList b))
-        <>  " : "
-        <>  prettyApplicationExpression (App Optional a)
+        <>  prettyApplicationExpression a
     prettyAnnotatedExpression (Note _ a) =
         prettyAnnotatedExpression a
     prettyAnnotatedExpression a0 =
diff --git a/src/Dhall/Set.hs b/src/Dhall/Set.hs
--- a/src/Dhall/Set.hs
+++ b/src/Dhall/Set.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 
 -- | This module only exports ways of constructing a Set,
@@ -12,24 +13,33 @@
     , toSet
     , toSeq
     , fromList
+    , fromSet
     , append
     , empty
     , difference
+    , sort
+    , isSorted
+    , null
     ) where
 
-import Prelude
+import Prelude hiding (null)
+import Control.DeepSeq (NFData)
 import Data.List (foldl')
 import Data.Sequence (Seq, (|>))
 import Data.Data (Data)
 import GHC.Generics (Generic)
+import Instances.TH.Lift ()
+import Language.Haskell.TH.Syntax (Lift)
 
 import qualified Data.Set
 import qualified Data.Sequence
 import qualified Data.Foldable
 
 data Set a = Set (Data.Set.Set a) (Seq a)
-    deriving (Eq, Generic, Ord, Show, Data)
+    deriving (Eq, Generic, Ord, Show, Data, NFData)
 
+instance (Data a, Lift a, Ord a) => Lift (Set a)
+
 instance Foldable Set where
     foldMap f = foldMap f . toSeq
 
@@ -48,6 +58,9 @@
 fromList :: Ord a => [a] -> Set a
 fromList = foldl' (flip append) empty
 
+fromSet :: Data.Set.Set a -> Set a
+fromSet s = Set s (Data.Sequence.fromList (Data.Set.elems s))
+
 -- O(log n) time complexity.
 append :: Ord a => a -> Set a -> Set a
 append x os@(Set s xs)
@@ -62,3 +75,29 @@
 difference :: Ord a => Set a -> Set a -> [a]
 difference os (Set s _) =
     filter (\ x -> not (Data.Set.member x s)) (toList os)
+
+{-| Sort the set elements, forgetting their original ordering.
+
+>>> sort (fromList [2, 1]) == fromList [1, 2]
+True
+-}
+sort :: Ord a => Set a -> Set a
+sort (Set s xs) = Set s (Data.Sequence.sort xs)
+
+{-|
+>>> isSorted (fromList [2, 1])
+False
+>>> isSorted (fromList [1, 2])
+True
+-}
+isSorted :: Ord a => Set a -> Bool
+isSorted s = toList s == Data.Set.toList (toSet s)
+
+{-|
+>>> null (fromList [1])
+False
+>>> null (fromList [])
+True
+-}
+null :: Set a -> Bool
+null (Set s _) = Data.Set.null s
diff --git a/src/Dhall/Src.hs b/src/Dhall/Src.hs
--- a/src/Dhall/Src.hs
+++ b/src/Dhall/Src.hs
@@ -1,6 +1,8 @@
+{-# LANGUAGE DeriveAnyClass     #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveGeneric      #-}
 {-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE TemplateHaskell    #-}
 
 -- | This module provides the `Src` type used for source spans in error messages
 
@@ -9,12 +11,15 @@
       Src(..)
     ) where
 
+import Control.DeepSeq (NFData)
 import Data.Data (Data)
 import Data.Monoid ((<>))
 import Data.Text (Text)
 import Data.Text.Prettyprint.Doc  (Pretty (..))
 import GHC.Generics (Generic)
-import Text.Megaparsec (SourcePos)
+import Instances.TH.Lift ()
+import Language.Haskell.TH.Syntax (Lift, lift)
+import Text.Megaparsec (SourcePos (SourcePos), mkPos, unPos)
 
 import {-# SOURCE #-} qualified Dhall.Util
 
@@ -25,8 +30,19 @@
 -- | Source code extract
 data Src = Src !SourcePos !SourcePos Text
   -- Text field is intentionally lazy
-  deriving (Data, Eq, Generic, Ord, Show)
+  deriving (Data, Eq, Generic, Ord, Show, NFData)
 
+
+instance Lift Src where
+    lift (Src (SourcePos a b c) (SourcePos d e f) g) =
+        [| Src (SourcePos a (mkPos b') (mkPos c')) (SourcePos d (mkPos e') (mkPos f')) g |]
+      where
+        b' = unPos b
+        c' = unPos c
+        e' = unPos e
+        f' = unPos f
+
+
 instance Pretty Src where
     pretty (Src begin _ text) =
             pretty (Dhall.Util.snip numberedLines)
@@ -55,6 +71,6 @@
             inputString = Text.unpack line
 
             outputString =
-                Printf.printf ("%" <> show numberWidth <> "d: %s") n inputString
+                Printf.printf ("%" <> show numberWidth <> "d│ %s") n inputString
 
         numberedLines = Text.unlines (zipWith adapt [minimumNumber..] ls)
diff --git a/src/Dhall/Tutorial.hs b/src/Dhall/Tutorial.hs
--- a/src/Dhall/Tutorial.hs
+++ b/src/Dhall/Tutorial.hs
@@ -1,7 +1,7 @@
 {-# OPTIONS_GHC -fno-warn-unused-imports #-}
 
 {-| Dhall is a programming language specialized for configuration files.  This
-    module contains a tutorial explaning how to author configuration files using
+    module contains a tutorial explaining how to author configuration files using
     this language
 -}
 module Dhall.Tutorial (
@@ -317,7 +317,7 @@
 -- - Bool
 -- + Natural
 -- ...
--- 1: 1 : Bool
+-- 1│ 1 : Bool
 -- ...
 -- (input):1:1
 -- ...
@@ -517,7 +517,7 @@
 -- *** Exception:
 -- ...Error...: An empty list requires a type annotation
 -- ...
--- 1: []
+-- 1│ []
 -- ...
 -- (input):1:1
 --
@@ -531,7 +531,7 @@
 -- - Natural
 -- + Bool
 -- ...
--- 1:     True
+-- 1│     True
 -- ...
 -- (input):1:5
 -- ...
diff --git a/src/Dhall/TypeCheck.hs b/src/Dhall/TypeCheck.hs
--- a/src/Dhall/TypeCheck.hs
+++ b/src/Dhall/TypeCheck.hs
@@ -21,25 +21,24 @@
     , TypeMessage(..)
     ) where
 
-import Control.Applicative (empty)
 import Control.Exception (Exception)
-import Data.Data (Data(..))
-import Data.Foldable (forM_, toList)
+import Data.Functor (void)
 import Data.List.NonEmpty (NonEmpty(..))
-import Data.Monoid (First(..))
+import Data.Monoid (Endo(..), First(..))
 import Data.Sequence (Seq, ViewL(..))
 import Data.Semigroup (Semigroup(..))
 import Data.Set (Set)
 import Data.Text (Text)
 import Data.Text.Prettyprint.Doc (Doc, Pretty(..))
-import Data.Traversable (forM)
 import Data.Typeable (Typeable)
-import Dhall.Binary (FromTerm(..), ToTerm(..))
+import Dhall.Binary (ToTerm(..))
 import Dhall.Core (Binding(..), Const(..), Chunks(..), Expr(..), Var(..))
 import Dhall.Context (Context)
 import Dhall.Pretty (Ann, layoutOpts)
+import Dhall.X (X(..))
 
 import qualified Data.Foldable
+import qualified Data.Map
 import qualified Data.Sequence
 import qualified Data.Set
 import qualified Data.Text                               as Text
@@ -195,7 +194,9 @@
             loop ctx b2
 
     loop ctx e@(Annot x t       ) = do
-        _ <- loop ctx t
+        case Dhall.Core.denote t of
+            Const _ -> return ()
+            _       -> void (loop ctx t)
 
         t' <- loop ctx x
         if Dhall.Core.judgmentallyEqual t t'
@@ -368,13 +369,13 @@
         return (Pi "_" (Const Type) (Const Type))
     loop ctx e@(ListLit  Nothing  xs) = do
         case Data.Sequence.viewl xs of
-            x0 :< _ -> do
+            x0 :< xs' -> do
                 t <- loop ctx x0
                 s <- fmap Dhall.Core.normalize (loop ctx t)
                 case s of
                     Const Type -> return ()
-                    _ -> Left (TypeError ctx e (InvalidListType t))
-                flip traverseWithIndex_ xs (\i x -> do
+                    _ -> Left (TypeError ctx e (InvalidListType (App List t)))
+                flip traverseWithIndex_ xs' (\i x -> do
                     t' <- loop ctx x
                     if Dhall.Core.judgmentallyEqual t t'
                         then return ()
@@ -385,20 +386,25 @@
                             Left (TypeError ctx x err) )
                 return (App List t)
             _ -> Left (TypeError ctx e MissingListType)
-    loop ctx e@(ListLit (Just t ) xs) = do
-        s <- fmap Dhall.Core.normalize (loop ctx t)
-        case s of
-            Const Type -> return ()
-            _ -> Left (TypeError ctx e (InvalidListType t))
+    loop ctx e@(ListLit (Just t0) xs) = do
+        _ <- loop ctx t0
+        let nf_t0 = Dhall.Core.normalize t0
+        t1 <- case nf_t0 of
+            App List t1 -> do
+                s <- fmap Dhall.Core.normalize (loop ctx t1)
+                case s of
+                    Const Type -> return t1
+                    _ -> Left (TypeError ctx e (InvalidListType nf_t0))
+            _ -> Left (TypeError ctx e (InvalidListType nf_t0))
         flip traverseWithIndex_ xs (\i x -> do
             t' <- loop ctx x
-            if Dhall.Core.judgmentallyEqual t t'
+            if Dhall.Core.judgmentallyEqual t1 t'
                 then return ()
                 else do
-                    let nf_t  = Dhall.Core.normalize t
+                    let nf_t  = Dhall.Core.normalize t1
                     let nf_t' = Dhall.Core.normalize t'
                     Left (TypeError ctx x (InvalidListElement i nf_t x nf_t')) )
-        return (App List t)
+        return (App List t1)
     loop ctx e@(ListAppend l r  ) = do
         tl <- fmap Dhall.Core.normalize (loop ctx l)
         el <- case tl of
@@ -446,20 +452,6 @@
         return (Pi "_" (Const Type) (Const Type))
     loop _      None              = do
         return (Pi "A" (Const Type) (App Optional "A"))
-    loop ctx e@(OptionalLit t xs) = do
-        s <- fmap Dhall.Core.normalize (loop ctx t)
-        case s of
-            Const Type -> return ()
-            _ -> Left (TypeError ctx e (InvalidOptionalType t))
-        forM_ xs (\x -> do
-            t' <- loop ctx x
-            if Dhall.Core.judgmentallyEqual t t'
-                then return ()
-                else do
-                    let nf_t  = Dhall.Core.normalize t
-                    let nf_t' = Dhall.Core.normalize t'
-                    Left (TypeError ctx e (InvalidOptionalElement nf_t x nf_t')) )
-        return (App Optional t)
     loop ctx e@(Some a) = do
         _A <- loop ctx a
         s <- fmap Dhall.Core.normalize (loop ctx _A)
@@ -500,27 +492,29 @@
                 Dhall.Map.unorderedTraverseWithKey_ process rest
                 return (Const c)
     loop ctx e@(RecordLit kvs   ) = do
-        case Dhall.Map.toList kvs of
-            []         -> return (Record mempty)
-            (k0, v0):_ -> do
+        case Dhall.Map.uncons kvs of
+            Nothing             -> return (Record mempty)
+            Just (k0, v0, kvs') -> do
                 t0 <- loop ctx v0
                 s0 <- fmap Dhall.Core.normalize (loop ctx t0)
                 c <- case s0 of
                     Const c -> pure c
-                    _       -> Left (TypeError ctx e (InvalidField k0 v0))
+                    _       -> Left (TypeError ctx e (InvalidFieldType k0 v0))
+
                 let process k v = do
                         t <- loop ctx v
                         s <- fmap Dhall.Core.normalize (loop ctx t)
                         case s of
-                            Const c' ->
-                                if c == c'
-                                then return ()
-                                else Left (TypeError ctx e (FieldMismatch k v c k0 v0 c'))
-                            _ -> Left (TypeError ctx e (InvalidField k t))
+                            Const c'
+                                | c == c'   -> return ()
+                                | otherwise -> Left (TypeError ctx e (FieldMismatch k v c k0 v0 c'))
+                            _ -> Left (TypeError ctx e (InvalidFieldType k t))
 
                         return t
-                kts <- Dhall.Map.traverseWithKey process kvs
-                return (Record kts)
+
+                kts <- Dhall.Map.unorderedTraverseWithKey process kvs'
+
+                return (Record (Dhall.Map.insert k0 t0 kts))
     loop ctx e@(Union     kts   ) = do
         let nonEmpty k mt = First (fmap (\t -> (k, t)) mt)
 
@@ -555,7 +549,7 @@
                             then return ()
                             else Left (TypeError ctx e (AlternativeAnnotationMismatch k t c k0 t0 c0))
 
-                Dhall.Map.unorderedTraverseWithKey_ process kts
+                Dhall.Map.unorderedTraverseWithKey_ process (Dhall.Map.delete k0 kts)
 
                 return (Const c0)
     loop ctx e@(UnionLit k v kts) = do
@@ -592,24 +586,14 @@
             else Left (TypeError ctx e (RecordMismatch '∧' kvsX kvsY constX constY))
 
         let combineTypes ktsL ktsR = do
-                let ksL =
-                        Data.Set.fromList (Dhall.Map.keys ktsL)
-                let ksR =
-                        Data.Set.fromList (Dhall.Map.keys ktsR)
-                let ks = Data.Set.union ksL ksR
-                kts <- forM (toList ks) (\k -> do
-                    case (Dhall.Map.lookup k ktsL, Dhall.Map.lookup k ktsR) of
-                        (Just (Record ktsL'), Just (Record ktsR')) -> do
-                            t <- combineTypes ktsL' ktsR'
-                            return (k, t)
-                        (Nothing, Just t) -> do
-                            return (k, t)
-                        (Just t, Nothing) -> do
-                            return (k, t)
-                        _ -> do
-                            Left (TypeError ctx e (FieldCollision k)) )
-                return (Record (Dhall.Map.fromList kts))
+                let combine _ (Record ktsL') (Record ktsR') = combineTypes ktsL' ktsR'
+                    combine k _ _ = Left (TypeError ctx e (FieldCollision k))
 
+                let eKts = Dhall.Map.outerJoin Right Right combine
+                                               ktsL ktsR
+
+                fmap Record (Dhall.Map.unorderedTraverseWithKey (\_k v -> v) eKts)
+
         combineTypes ktsX ktsY
     loop ctx e@(CombineTypes l r) = do
         tL <- loop ctx l
@@ -640,22 +624,14 @@
             _          -> Left (TypeError ctx e (CombineTypesRequiresRecordType r r'))
 
         let combineTypes ktsL ktsR = do
-                let ksL =
-                        Data.Set.fromList (Dhall.Map.keys ktsL)
-                let ksR =
-                        Data.Set.fromList (Dhall.Map.keys ktsR)
-                let ks = Data.Set.union ksL ksR
-                forM_ (toList ks) (\k -> do
-                    case (Dhall.Map.lookup k ktsL, Dhall.Map.lookup k ktsR) of
-                        (Just (Record ktsL'), Just (Record ktsR')) -> do
-                            combineTypes ktsL' ktsR'
-                        (Nothing, Just _) -> do
-                            return ()
-                        (Just _, Nothing) -> do
-                            return ()
-                        _ -> do
-                            Left (TypeError ctx e (FieldCollision k)) )
+                let mL = Dhall.Map.toMap ktsL
+                let mR = Dhall.Map.toMap ktsR
 
+                let combine _ (Record ktsL') (Record ktsR') = combineTypes ktsL' ktsR'
+                    combine k _ _ = Left (TypeError ctx e (FieldCollision k))
+
+                Data.Foldable.sequence_ (Data.Map.intersectionWithKey combine mL mR)
+
         combineTypes ktsL0 ktsR0
 
         return (Const c)
@@ -698,8 +674,8 @@
             Union kts -> return kts
             _         -> Left (TypeError ctx e (MustMergeUnion kvsY tKvsY))
 
-        let ksX = Data.Set.fromList (Dhall.Map.keys ktsX)
-        let ksY = Data.Set.fromList (Dhall.Map.keys ktsY)
+        let ksX = Dhall.Map.keysSet ktsX
+        let ksY = Dhall.Map.keysSet ktsY
 
         let diffX = Data.Set.difference ksX ksY
         let diffY = Data.Set.difference ksY ksX
@@ -771,6 +747,37 @@
         Dhall.Map.unorderedTraverseWithKey_ process ktsY
 
         return _T₁
+
+    loop ctx e@(ToMap kvsX mT₁) = do
+        tKvsX <- fmap Dhall.Core.normalize (loop ctx kvsX)
+
+        ktsX <- case tKvsX of
+            Record kts -> return kts
+            _          -> Left (TypeError ctx e (MustMapARecord kvsX tKvsX))
+
+        Data.Foldable.traverse_ (loop ctx) mT₁
+
+        let ktX = appEndo (foldMap (Endo . compareFieldTypes) ktsX) Nothing
+            mT₂ = fmap Dhall.Core.normalize mT₁
+            mapType fieldType = App List (Record $ Dhall.Map.fromList [("mapKey", Text),
+                                                                       ("mapValue", fieldType)])
+            compareFieldTypes t Nothing = Just (Right t)
+            compareFieldTypes t r@(Just (Right t'))
+               | Dhall.Core.judgmentallyEqual t t' = r
+               | otherwise = Just (Left $ TypeError ctx e (HeterogenousRecordToMap tKvsX t t'))
+            compareFieldTypes _ r@(Just Left{}) = r
+
+        case (ktX, mT₂) of
+            (Nothing, Nothing) -> Left (TypeError ctx e MissingToMapType)
+            (Just err@Left{}, _) -> err
+            (Just (Right t), Nothing) -> pure (mapType t)
+            (Nothing, Just t@(App List (Record mapItemType)))
+               | Just fieldType <- Dhall.Map.lookup "mapValue" mapItemType,
+                 Dhall.Core.judgmentallyEqual t (mapType fieldType) -> pure t
+            (Nothing, Just t) -> Left (TypeError ctx e $ InvalidToMapType t)
+            (Just (Right t₁), Just t₂)
+               | Dhall.Core.judgmentallyEqual (mapType t₁) t₂ -> pure t₂
+               | otherwise -> Left (TypeError ctx e $ MapTypeMismatch (mapType t₁) t₂)
     loop ctx e@(Field r x       ) = do
         t <- fmap Dhall.Core.normalize (loop ctx r)
 
@@ -789,7 +796,7 @@
                     case Dhall.Map.lookup x kts of
                         Just (Just t') -> return (Pi x t' (Union kts))
                         Just Nothing   -> return (Union kts)
-                        Nothing -> Left (TypeError ctx e (MissingField x t))
+                        Nothing -> Left (TypeError ctx e (MissingConstructor x r))
                   r' -> Left (TypeError ctx e (CantAccess text r' t))
     loop ctx e@(Project r (Left xs)) = do
         t <- fmap Dhall.Core.normalize (loop ctx r)
@@ -812,22 +819,33 @@
 
                 Left (TypeError ctx e (CantProject text r t))
     loop ctx e@(Project r (Right t)) = do
-        _R <- loop ctx r
+        _R <- fmap Dhall.Core.normalize (loop ctx r)
 
         case _R of
             Record ktsR -> do
-                _ <- fmap Dhall.Core.normalize (loop ctx t)
+                _ <- loop ctx t
 
                 case Dhall.Core.normalize t of
                     Record ktsT -> do
-                        let keysR = Dhall.Set.fromList (Dhall.Map.keys ktsR)
-                        let keysT = Dhall.Set.fromList (Dhall.Map.keys ktsT)
+                        let actualSubset =
+                                Record (Dhall.Map.intersection ktsR ktsT)
 
-                        case Dhall.Set.difference keysT keysR of
-                            k : _ -> do
-                                Left (TypeError ctx e (MissingField k t))
-                            [] -> do
-                                return (Record ktsT)
+                        let expectedSubset = t
+
+                        let process k tT = do
+                                case Dhall.Map.lookup k ktsR of
+                                    Nothing -> do
+                                        Left (TypeError ctx e (MissingField k _R))
+                                    Just tR -> do
+                                        if Dhall.Core.judgmentallyEqual tT tR
+                                            then do
+                                                return ()
+                                            else do
+                                                Left (TypeError ctx e (ProjectionTypeMismatch k tT tR expectedSubset actualSubset))
+
+                        Dhall.Map.unorderedTraverseWithKey_ process ktsT
+
+                        return (Record ktsT)
                     _ -> do
                         Left (TypeError ctx e (CantProjectByExpression t))
 
@@ -850,29 +868,6 @@
 typeOf :: Expr s X -> Either (TypeError s X) (Expr s X)
 typeOf = typeWith Dhall.Context.empty
 
--- | Like `Data.Void.Void`, except with a shorter inferred type
-newtype X = X { absurd :: forall a . a }
-
-instance Show X where
-    show = absurd
-
-instance Eq X where
-  _ == _ = True
-
-instance Data X where
-    dataTypeOf = absurd
-    gunfold _ _ _ = undefined
-    toConstr = absurd
-
-instance Pretty X where
-    pretty = absurd
-
-instance FromTerm X where
-    decode _ = empty
-
-instance ToTerm X where
-    encode = absurd
-
 -- | The specific type error
 data TypeMessage s a
     = UnboundVariable Text
@@ -886,13 +881,10 @@
     | MismatchedListElements Int (Expr s a) (Expr s a) (Expr s a)
     | InvalidListElement Int (Expr s a) (Expr s a) (Expr s a)
     | InvalidListType (Expr s a)
-    | InvalidOptionalElement (Expr s a) (Expr s a) (Expr s a)
-    | InvalidOptionalType (Expr s a)
     | InvalidSome (Expr s a) (Expr s a) (Expr s a)
     | InvalidPredicate (Expr s a) (Expr s a)
     | IfBranchMismatch (Expr s a) (Expr s a) (Expr s a) (Expr s a)
     | IfBranchMustBeTerm Bool (Expr s a) (Expr s a) (Expr s a)
-    | InvalidField Text (Expr s a)
     | InvalidFieldType Text (Expr s a)
     | FieldAnnotationMismatch Text (Expr s a) Const Text (Expr s a) Const
     | FieldMismatch Text (Expr s a) Const Text (Expr s a) Const
@@ -908,6 +900,11 @@
     | FieldCollision Text
     | MustMergeARecord (Expr s a) (Expr s a)
     | MustMergeUnion (Expr s a) (Expr s a)
+    | MustMapARecord (Expr s a) (Expr s a)
+    | HeterogenousRecordToMap (Expr s a) (Expr s a) (Expr s a)
+    | InvalidToMapType (Expr s a)
+    | MapTypeMismatch (Expr s a) (Expr s a)
+    | MissingToMapType
     | UnusedHandler (Set Text)
     | MissingHandler (Set Text)
     | HandlerInputTypeMismatch Text (Expr s a) (Expr s a)
@@ -915,11 +912,12 @@
     | InvalidHandlerOutputType Text (Expr s a) (Expr s a)
     | MissingMergeType
     | HandlerNotAFunction Text (Expr s a)
-    | ConstructorsRequiresAUnionType (Expr s a) (Expr s a)
     | CantAccess Text (Expr s a) (Expr s a)
     | CantProject Text (Expr s a) (Expr s a)
     | CantProjectByExpression (Expr s a)
     | MissingField Text (Expr s a)
+    | MissingConstructor Text (Expr s a)
+    | ProjectionTypeMismatch Text (Expr s a) (Expr s a) (Expr s a) (Expr s a)
     | CantAnd (Expr s a) (Expr s a)
     | CantOr (Expr s a) (Expr s a)
     | CantEQ (Expr s a) (Expr s a)
@@ -959,9 +957,6 @@
 insert :: Pretty a => a -> Doc Ann
 insert = Dhall.Util.insert
 
-prettyDiff :: (Eq a, Pretty a, ToTerm a) => Expr s a -> Expr s a -> Doc Ann
-prettyDiff exprL exprR = Dhall.Diff.diffNormalized exprL exprR
-
 prettyTypeMessage
     :: (Eq a, Pretty a, ToTerm a) => TypeMessage s a -> ErrorMessages
 prettyTypeMessage (UnboundVariable x) = ErrorMessages {..}
@@ -1369,7 +1364,7 @@
   where
     short = "Wrong type of function argument\n"
         <>  "\n"
-        <>  prettyDiff expr1 expr3
+        <>  Dhall.Diff.diffNormalized expr1 expr3
 
     long =
         "Explanation: Every function declares what type or kind of argument to accept    \n\
@@ -1503,7 +1498,7 @@
   where
     short = "Expression doesn't match annotation\n"
         <>  "\n"
-        <>  prettyDiff expr1 expr2
+        <>  Dhall.Diff.diffNormalized expr1 expr2
     long =
         "Explanation: You can annotate an expression with its type or kind using the     \n\
         \❰:❱ symbol, like this:                                                          \n\
@@ -1786,7 +1781,7 @@
   where
     short = "❰if❱ branches must have matching types\n"
         <>  "\n"
-        <>  prettyDiff expr1 expr3
+        <>  Dhall.Diff.diffNormalized expr1 expr3
 
     long =
         "Explanation: Every ❰if❱ expression has a ❰then❱ and ❰else❱ branch, each of which\n\
@@ -1860,11 +1855,11 @@
 
 prettyTypeMessage (InvalidListType expr0) = ErrorMessages {..}
   where
-    short = "Invalid type for ❰List❱ elements"
+    short = "Invalid type for ❰List❱"
 
     long =
-        "Explanation: ❰List❱s can optionally document the type of their elements with a  \n\
-        \type annotation, like this:                                                     \n\
+        "Explanation: ❰List❱s can optionally document their type with a type annotation, \n\
+        \like this:                                                                      \n\
         \                                                                                \n\
         \                                                                                \n\
         \    ┌──────────────────────────┐                                                \n\
@@ -1878,10 +1873,21 @@
         \    ┌───────────────────┐                                                       \n\
         \    │ [] : List Natural │  An empty ❰List❱                                      \n\
         \    └───────────────────┘                                                       \n\
-        \                ⇧                                                               \n\
-        \                You must specify the type when the ❰List❱ is empty              \n\
+        \           ⇧                                                                    \n\
+        \           You must specify the type when the ❰List❱ is empty                   \n\
         \                                                                                \n\
         \                                                                                \n\
+        \The type must be of the form ❰List ...❱ and not something else.  For example,   \n\
+        \the following type annotation is " <> _NOT <> " valid:                          \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \    ┌────────────┐                                                              \n\
+        \    │ ... : Bool │                                                              \n\
+        \    └────────────┘                                                              \n\
+        \            ⇧                                                                   \n\
+        \            This type does not have the form ❰List ...❱                         \n\
+        \                                                                                \n\
+        \                                                                                \n\
         \The element type must be a type and not something else.  For example, the       \n\
         \following element types are " <> _NOT <> " valid:                               \n\
         \                                                                                \n\
@@ -1900,11 +1906,11 @@
         \                 This is a ❰Kind❱ and not a ❰Type❱                              \n\
         \                                                                                \n\
         \                                                                                \n\
-        \You declared that the ❰List❱'s elements should have type:                       \n\
+        \You declared that the ❰List❱ should have type:                                  \n\
         \                                                                                \n\
         \" <> txt0 <> "\n\
         \                                                                                \n\
-        \... which is not a ❰Type❱                                                       \n"
+        \... which is not a valid list type                                              \n"
       where
         txt0 = insert expr0
 
@@ -1938,7 +1944,7 @@
   where
     short = "List elements should all have the same type\n"
         <>  "\n"
-        <>  prettyDiff expr0 expr2
+        <>  Dhall.Diff.diffNormalized expr0 expr2
 
     long =
         "Explanation: Every element in a list must have the same type                    \n\
@@ -1976,7 +1982,7 @@
   where
     short = "List element has the wrong type\n"
         <>  "\n"
-        <>  prettyDiff expr0 expr2
+        <>  Dhall.Diff.diffNormalized expr0 expr2
 
     long =
         "Explanation: Every element in the list must have a type matching the type       \n\
@@ -2010,99 +2016,6 @@
         txt1 = pretty i
         txt3 = insert expr2
 
-prettyTypeMessage (InvalidOptionalType expr0) = ErrorMessages {..}
-  where
-    short = "Invalid type for ❰Optional❱ element"
-
-    long =
-        "Explanation: The legacy ❰List❱-like syntax for ❰Optional❱ literals ends with a  \n\
-        \type annotation for the element that might be present, like this:               \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \    ┌────────────────────────┐                                                  \n\
-        \    │ [1] : Optional Natural │  An optional element that's present              \n\
-        \    └────────────────────────┘                                                  \n\
-        \                     ⇧                                                          \n\
-        \                     The type of the ❰Optional❱ element, which is an ❰Natural❱  \n\
-        \                     number                                                     \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \    ┌────────────────────────┐                                                  \n\
-        \    │ [] : Optional Natural  │  An optional element that's absent               \n\
-        \    └────────────────────────┘                                                  \n\
-        \                    ⇧                                                           \n\
-        \                    You still specify the type even when the element is absent  \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \The element type must be a type and not something else.  For example, the       \n\
-        \following element types are " <> _NOT <> " valid:                               \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \    ┌──────────────────┐                                                        \n\
-        \    │ ... : Optional 1 │                                                        \n\
-        \    └──────────────────┘                                                        \n\
-        \                     ⇧                                                          \n\
-        \                     This is a ❰Natural❱ number and not a ❰Type❱                \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \    ┌─────────────────────┐                                                     \n\
-        \    │ ... : Optional Type │                                                     \n\
-        \    └─────────────────────┘                                                     \n\
-        \                     ⇧                                                          \n\
-        \                     This is a ❰Kind❱ and not a ❰Type❱                          \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \Even if the element is absent you still must specify a valid type               \n\
-        \                                                                                \n\
-        \You declared that the ❰Optional❱ element should have type:                      \n\
-        \                                                                                \n\
-        \" <> txt0 <> "\n\
-        \                                                                                \n\
-        \... which is not a ❰Type❱                                                       \n"
-      where
-        txt0 = insert expr0
-
-prettyTypeMessage (InvalidOptionalElement expr0 expr1 expr2) = ErrorMessages {..}
-  where
-    short = "❰Optional❱ element has the wrong type\n"
-        <>  "\n"
-        <>  prettyDiff expr0 expr2
-
-    long =
-        "Explanation: An ❰Optional❱ element must have a type matching the type annotation\n\
-        \                                                                                \n\
-        \For example, this is a valid ❰Optional❱ value:                                  \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \    ┌────────────────────────┐                                                  \n\
-        \    │ [1] : Optional Natural │  ❰1❱ is a ❰Natural❱ number, which matches the    \n\
-        \    └────────────────────────┘  number                                          \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \... but this is " <> _NOT <> " a valid ❰Optional❱ value:                        \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \    ┌────────────────────────────┐                                              \n\
-        \    │ [\"ABC\"] : Optional Natural │  ❰\"ABC\"❱ is not a ❰Natural❱ number       \n\
-        \    └────────────────────────────┘                                              \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \Your ❰Optional❱ element should have this type:                                  \n\
-        \                                                                                \n\
-        \" <> txt0 <> "\n\
-        \                                                                                \n\
-        \... but the element you provided:                                               \n\
-        \                                                                                \n\
-        \" <> txt1 <> "\n\
-        \                                                                                \n\
-        \... has this type instead:                                                      \n\
-        \                                                                                \n\
-        \" <> txt2 <> "\n"
-      where
-        txt0 = insert expr0
-        txt1 = insert expr1
-        txt2 = insert expr2
-
 prettyTypeMessage (InvalidSome expr0 expr1 expr2) = ErrorMessages {..}
   where
     short = "❰Some❱ argument has the wrong type"
@@ -2304,43 +2217,6 @@
         level Kind = "type"
         level Sort = "kind"
 
-prettyTypeMessage (InvalidField k expr0) = ErrorMessages {..}
-  where
-    short = "Invalid field"
-
-    long =
-        "Explanation: Every record literal is a set of fields assigned to values, like   \n\
-        \this:                                                                           \n\
-        \                                                                                \n\
-        \    ┌────────────────────────────────────────┐                                  \n\
-        \    │ { foo = 100, bar = True, baz = \"ABC\" } │                                \n\
-        \    └────────────────────────────────────────┘                                  \n\
-        \                                                                                \n\
-        \However, fields can only be terms and or ❰Type❱s and not ❰Kind❱s                \n\
-        \                                                                                \n\
-        \For example, the following record literal is " <> _NOT <> " valid:              \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \    ┌────────────────┐                                                          \n\
-        \    │ { foo = Type } │                                                          \n\
-        \    └────────────────┘                                                          \n\
-        \              ⇧                                                                 \n\
-        \              ❰Type❱ is a ❰Kind❱, which is not allowed                          \n\
-        \                                                                                \n\
-        \                                                                                \n\
-        \You provided a record literal with a field named:                               \n\
-        \                                                                                \n\
-        \" <> txt0 <> "\n\
-        \                                                                                \n\
-        \... whose value is:                                                             \n\
-        \                                                                                \n\
-        \" <> txt1 <> "\n\
-        \                                                                                \n\
-        \... which is not a term or ❰Type❱                                               \n"
-      where
-        txt0 = insert k
-        txt1 = insert expr0
-
 prettyTypeMessage (InvalidAlternativeType k expr0) = ErrorMessages {..}
   where
     short = "Invalid alternative type"
@@ -2506,7 +2382,7 @@
   where
     short = "You can only append ❰List❱s with matching element types\n"
         <>  "\n"
-        <>  prettyDiff expr0 expr1
+        <>  Dhall.Diff.diffNormalized expr0 expr1
 
     long =
         "Explanation: You can append two ❰List❱s using the ❰#❱ operator, like this:      \n\
@@ -3060,7 +2936,7 @@
   where
     short = "Wrong handler input type\n"
         <>  "\n"
-        <>  prettyDiff expr1 expr2
+        <>  Dhall.Diff.diffNormalized expr1 expr2
 
     long =
         "Explanation: You can ❰merge❱ the alternatives of a union using a record with one\n\
@@ -3122,7 +2998,7 @@
   where
     short = "Wrong handler output type\n"
         <>  "\n"
-        <>  prettyDiff expr1 expr2
+        <>  Dhall.Diff.diffNormalized expr1 expr2
 
     long =
         "Explanation: You can ❰merge❱ the alternatives of a union using a record with one\n\
@@ -3186,7 +3062,7 @@
   where
     short = "Handlers should have the same output type\n"
         <>  "\n"
-        <>  prettyDiff expr0 expr1
+        <>  Dhall.Diff.diffNormalized expr0 expr1
 
     long =
         "Explanation: You can ❰merge❱ the alternatives of a union using a record with one\n\
@@ -3279,57 +3155,99 @@
         txt0 = insert k
         txt1 = insert expr0
 
-prettyTypeMessage (ConstructorsRequiresAUnionType expr0 expr1) = ErrorMessages {..}
+prettyTypeMessage (MustMapARecord _expr0 _expr1) = ErrorMessages {..}
   where
-    short = "❰constructors❱ requires a union type"
+    short = "❰toMap❱ expects a record value"
 
     long =
-        "Explanation: You can only use the ❰constructors❱ keyword on an argument that is \n\
-        \a union type literal, like this:                                                \n\
+        "Explanation: You can apply ❰toMap❱ to any homogenous record, like this:         \n\
         \                                                                                \n\
         \                                                                                \n\
-        \    ┌───────────────────────────────────────────────┐                           \n\
-        \    │ constructors < Left : Natural, Right : Bool > │                           \n\
-        \    └───────────────────────────────────────────────┘                           \n\
+        \    ┌─────────────────────────────────────────────────────────────────────┐     \n\
+        \    │     let record = { one = 1, two = 2 }                               │     \n\
+        \    │ in  toMap record : List { mapKey : Text, mapValue : Natural}        │     \n\
+        \    └─────────────────────────────────────────────────────────────────────┘     \n\
         \                                                                                \n\
         \                                                                                \n\
-        \... but you cannot use the ❰constructors❱ keyword on any other type of argument.\n\
-        \For example, you cannot use a variable argument:                                \n\
+        \... but the argument to ❰toMap❱ must be a record and not some other type.       \n"
+
+prettyTypeMessage (HeterogenousRecordToMap _expr0 _expr1 _expr2) = ErrorMessages {..}
+  where
+    short = "❰toMap❱ expects a homogenous record"
+
+    long =
+        "Explanation: You can apply ❰toMap❱ to any homogenous record, like this:         \n\
         \                                                                                \n\
         \                                                                                \n\
-        \    ┌──────────────────────────────┐                                            \n\
-        \    │ λ(t : Type) → constructors t │  Invalid: ❰t❱ might not be a union type    \n\
-        \    └──────────────────────────────┘                                            \n\
+        \    ┌─────────────────────────────────────────────────────────────────────┐     \n\
+        \    │     let record = { one = 1, two = 2 }                               │     \n\
+        \    │ in  toMap record : List { mapKey : Text, mapValue : Natural}        │     \n\
+        \    └─────────────────────────────────────────────────────────────────────┘     \n\
         \                                                                                \n\
         \                                                                                \n\
-        \    ┌─────────────────────────────────────────────────┐                         \n\
-        \    │ let t : Type = < Left : Natural, Right : Bool > │  Invalid: Type-checking \n\
-        \    │ in  constructors t                              │  precedes normalization \n\
-        \    └─────────────────────────────────────────────────┘                         \n\
+        \... but every field of the record must have the same type.                      \n\
         \                                                                                \n\
+        \For example, the following expression is " <> _NOT <> " valid:                  \n\
         \                                                                                \n\
-        \However, you can import the union type argument:                                \n\
         \                                                                                \n\
+        \    ┌─────────────────────────────────────────┐                                 \n\
+        \    │ toMap { Foo = True, Bar = 0 }           │                                 \n\
+        \    └─────────────────────────────────────────┘                                 \n\
+        \                    ⇧           ⇧                                               \n\
+        \                    Bool        Natural                                         \n"
+
+prettyTypeMessage (MapTypeMismatch expr0 expr1) = ErrorMessages {..}
+  where
+    short = "❰toMap❱ result type doesn't match annotation"
+        <>  "\n"
+        <>  Dhall.Diff.diffNormalized expr0 expr1
+
+    long =
+        "Explanation: a ❰toMap❱ application has been annotated with a type that doesn't  \n\
+        \match its inferred type.                                                        \n"
+
+prettyTypeMessage (InvalidToMapType expr) =
+    ErrorMessages {..}
+  where
+    short = "An empty ❰toMap❱ was annotated with an invalid type"
+        <>  "\n"
+        <>  insert expr
+
+    long =
+        "Explanation: A ❰toMap❱ applied to an empty record must have a type annotation:  \n\
+        \that matches a list of key-value pairs, like this                               \n\
         \                                                                                \n\
-        \    ┌────────────────────────────────┐                                          \n\
-        \    │ constructors ./unionType.dhall │ Valid: Import resolution precedes        \n\
-        \    └────────────────────────────────┘ type-checking                            \n\
         \                                                                                \n\
+        \    ┌─────────────────────────────────────────────────────────────────────┐     \n\
+        \    │ toMap {=} : List { mapKey : Text, mapValue : Natural}               │     \n\
+        \    └─────────────────────────────────────────────────────────────────────┘     \n\
         \                                                                                \n\
-        \────────────────────────────────────────────────────────────────────────────────\n\
+        \The type you have provided doesn't match the expected form.                     \n\
+        \                                                                                \n"
+
+prettyTypeMessage MissingToMapType =
+    ErrorMessages {..}
+  where
+    short = "An empty ❰toMap❱ requires a type annotation"
+
+    long =
+        "Explanation: A ❰toMap❱ does not require a type annotation if the record has at  \n\
+        \least one field, like this                                                      \n\
         \                                                                                \n\
-        \You tried to supply the following argument:                                     \n\
         \                                                                                \n\
-        \" <> txt0 <> "\n\
+        \    ┌─────────────────────────────────────────────────────────────────────┐     \n\
+        \    │     let record = { one = 1, two = 2 }                               │     \n\
+        \    │ in  toMap record                                                    │     \n\
+        \    └─────────────────────────────────────────────────────────────────────┘     \n\
         \                                                                                \n\
-        \... which normalized to:                                                        \n\
         \                                                                                \n\
-        \" <> txt1 <> "\n\
+        \However, you must provide a type annotation with an empty record:               \n\
         \                                                                                \n\
-        \... which is not a union type literal                                           \n"
-      where
-        txt0 = insert expr0
-        txt1 = insert expr1
+        \                                                                                \n\
+        \    ┌─────────────────────────────────────────────────────────────────────┐     \n\
+        \    │ toMap {=} : List { mapKey : Text, mapValue : Natural}               │     \n\
+        \    └─────────────────────────────────────────────────────────────────────┘     \n\
+        \                                                                                \n"
 
 prettyTypeMessage (CantAccess lazyText0 expr0 expr1) = ErrorMessages {..}
   where
@@ -3531,12 +3449,14 @@
         \                                                                                \n\
         \For example, the following expression is " <> _NOT <> " valid:                  \n\
         \                                                                                \n\
+        \                                                                                \n\
         \    ┌─────────────────────────────────┐                                         \n\
         \    │ { foo = True, bar = \"ABC\" }.qux │                                       \n\
         \    └─────────────────────────────────┘                                         \n\
         \                                  ⇧                                             \n\
         \                                  Invalid: the record has no ❰qux❱ field        \n\
         \                                                                                \n\
+        \                                                                                \n\
         \You tried to access a field named:                                              \n\
         \                                                                                \n\
         \" <> txt0 <> "\n\
@@ -3548,6 +3468,91 @@
       where
         txt0 = insert k
         txt1 = insert expr0
+
+prettyTypeMessage (MissingConstructor k expr0) = ErrorMessages {..}
+  where
+    short = "Missing constructor"
+
+    long =
+        "Explanation: You can access constructors from unions, like this:                \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \    ┌───────────────────┐                                                       \n\
+        \    │ < Foo | Bar >.Foo │  This is valid ...                                    \n\
+        \    └───────────────────┘                                                       \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \... but you can only access constructors if they match an union alternative of  \n\
+        \the same name.                                                                  \n\
+        \                                                                                \n\
+        \For example, the following expression is " <> _NOT <> " valid:                  \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \    ┌───────────────────┐                                                       \n\
+        \    │ < Foo | Bar >.Baz │                                                       \n\
+        \    └───────────────────┘                                                       \n\
+        \                    ⇧                                                           \n\
+        \                    Invalid: the union has no ❰Baz❱ alternative                 \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \You tried to access a constructor named:                                        \n\
+        \                                                                                \n\
+        \" <> txt0 <> "\n\
+        \                                                                                \n\
+        \... but the constructor is missing because the union only defines the following \n\
+        \alternatives:                                                                   \n\
+        \                                                                                \n\
+        \" <> txt1 <> "\n"
+      where
+        txt0 = insert k
+        txt1 = insert expr0
+
+prettyTypeMessage (ProjectionTypeMismatch k expr0 expr1 expr2 expr3) = ErrorMessages {..}
+  where
+    short = "Projection type mismatch\n"
+        <>  "\n"
+        <>  Dhall.Diff.diffNormalized expr2 expr3
+
+    long =
+        "Explanation: You can project a subset of fields from a record by specifying the \n\
+        \desired type of the final record, like this:                                    \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \    ┌─────────────────────────────────────────────┐                             \n\
+        \    │ { foo = 1, bar = True }.({ foo : Natural }) │  This is valid              \n\
+        \    └─────────────────────────────────────────────┘                             \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \... but the expected type for each desired field must match the actual type of  \n\
+        \the corresponding field in the original record.                                 \n\
+        \                                                                                \n\
+        \For example, the following expression is " <> _NOT <> " valid:                  \n\
+        \                                                                                \n\
+        \              Invalid: The ❰foo❱ field contains ❰1❱, which has type ❰Natural❱...\n\
+        \              ⇩                                                                 \n\
+        \    ┌──────────────────────────────────────────┐                                \n\
+        \    │ { foo = 1, bar = True }.({ foo : Text }) │                                \n\
+        \    └──────────────────────────────────────────┘                                \n\
+        \                                       ⇧                                        \n\
+        \                                       ... but we requested that the ❰foo❱ field\n\
+        \                                       must contain a value of type ❰Text❱      \n\
+        \                                                                                \n\
+        \                                                                                \n\
+        \You tried to project out a field named:                                         \n\
+        \                                                                                \n\
+        \" <> txt0 <> "\n\
+        \                                                                                \n\
+        \... that should have type:                                                      \n\
+        \                                                                                \n\
+        \" <> txt1 <> "\n\
+        \                                                                                \n\
+        \... but that field instead had a value of type:                                 \n\
+        \                                                                                \n\
+        \" <> txt2 <> "\n"
+      where
+        txt0 = insert k
+        txt1 = insert expr0
+        txt2 = insert expr1
 
 prettyTypeMessage (CantAnd expr0 expr1) =
         buildBooleanOperator "&&" expr0 expr1
diff --git a/src/Dhall/X.hs b/src/Dhall/X.hs
new file mode 100644
--- /dev/null
+++ b/src/Dhall/X.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE RankNTypes #-}
+
+module Dhall.X where
+
+import Data.Data (Data(..))
+import Data.Text.Prettyprint.Doc (Pretty(..))
+import Instances.TH.Lift ()
+import Language.Haskell.TH.Syntax (Lift, lift)
+
+-- | Like `Data.Void.Void`, except with a shorter inferred type
+newtype X = X { absurd :: forall a . a }
+
+instance Show X where
+    show = absurd
+
+instance Eq X where
+  _ == _ = True
+
+instance Data X where
+    dataTypeOf = absurd
+    gunfold _ _ _ = undefined
+    toConstr = absurd
+
+instance Pretty X where
+    pretty = absurd
+
+instance Lift X where
+  lift (X impossible) = impossible
diff --git a/tests/Dhall/Test/Dhall.hs b/tests/Dhall/Test/Dhall.hs
--- a/tests/Dhall/Test/Dhall.hs
+++ b/tests/Dhall/Test/Dhall.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DeriveAnyClass      #-}
 {-# LANGUAGE DeriveGeneric       #-}
+{-# LANGUAGE FlexibleContexts    #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE RecordWildCards     #-}
 {-# LANGUAGE ScopedTypeVariables #-}
@@ -45,7 +46,7 @@
               , ( "foo", Dhall.Core.Text )
               ]
             )
-        extract _ = Nothing
+        extract expr = Dhall.typeError expected expr
 
 shouldShowDetailedTypeError :: TestTree
 shouldShowDetailedTypeError = testCase "detailed TypeError" $ do
diff --git a/tests/Dhall/Test/Diff.hs b/tests/Dhall/Test/Diff.hs
new file mode 100644
--- /dev/null
+++ b/tests/Dhall/Test/Diff.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Dhall.Test.Diff where
+
+import Data.Monoid ((<>))
+import Data.Text (Text)
+import Prelude hiding (FilePath)
+import Test.Tasty (TestTree)
+import Turtle (FilePath)
+
+import qualified Data.Text                             as Text
+import qualified Data.Text.IO                          as Text.IO
+import qualified Data.Text.Prettyprint.Doc             as Pretty
+import qualified Data.Text.Prettyprint.Doc.Render.Text as Pretty.Text
+import qualified Dhall.Core                            as Core
+import qualified Dhall.Diff                            as Diff
+import qualified Dhall.Parser                          as Parser
+import qualified Dhall.Test.Util                       as Test.Util
+import qualified Test.Tasty                            as Tasty
+import qualified Test.Tasty.HUnit                      as Tasty.HUnit
+import qualified Turtle
+
+diffDirectory :: FilePath
+diffDirectory = "./tests/diff"
+
+getTests :: IO TestTree
+getTests = do
+    diffTests <- Test.Util.discover (Turtle.chars <* "A.dhall") diffTest (Turtle.lstree diffDirectory)
+
+    let testTree = Tasty.testGroup "diff tests" [ diffTests ]
+
+    return testTree
+
+diffTest :: Text -> TestTree
+diffTest prefix =
+    Tasty.HUnit.testCase (Text.unpack prefix) $ do
+        let leftFile  = Text.unpack (prefix <> "A.dhall")
+        let rightFile = Text.unpack (prefix <> "B.dhall")
+        let diffFile  = Text.unpack (prefix <> ".txt")
+
+        let toInput file = do
+                text <- Text.IO.readFile file
+                Core.throws (Parser.exprFromText mempty text)
+
+        leftInput  <- toInput leftFile
+        rightInput <- toInput rightFile
+
+        expectedDiffText <- Text.IO.readFile diffFile
+
+        let actualDiffDocument =
+                Diff.diffNormalized leftInput rightInput <> "\n"
+
+        let options =
+                Pretty.LayoutOptions
+                    { Pretty.layoutPageWidth = Pretty.Unbounded }
+
+        let actualDiffText =
+                Pretty.Text.renderStrict
+                    (Pretty.layoutPretty options actualDiffDocument)
+
+        let message = "The diffed expressions did not match the expected output"
+
+        Tasty.HUnit.assertEqual message expectedDiffText actualDiffText
diff --git a/tests/Dhall/Test/Import.hs b/tests/Dhall/Test/Import.hs
--- a/tests/Dhall/Test/Import.hs
+++ b/tests/Dhall/Test/Import.hs
@@ -2,7 +2,6 @@
 
 module Dhall.Test.Import where
 
-import Control.Exception (catch)
 import Data.Monoid ((<>))
 import Data.Text (Text)
 import Dhall.Import (MissingImports(..))
@@ -11,6 +10,7 @@
 import Test.Tasty (TestTree)
 import Turtle (FilePath, (</>))
 
+import qualified Control.Exception                as Exception
 import qualified Control.Monad.Trans.State.Strict as State
 import qualified Data.Text                        as Text
 import qualified Data.Text.IO                     as Text.IO
@@ -52,8 +52,23 @@
 
         actualExpr <- Core.throws (Parser.exprFromText mempty text)
 
-        _ <- State.evalStateT (Import.loadWith actualExpr) (Import.emptyStatus directoryString)
+        let setCache =
+                Turtle.export "XDG_CACHE_HOME" "dhall-lang/tests/import/cache"
 
+        let unsetCache = Turtle.unset "XDG_CACHE_HOME"
+
+        let load =
+                State.evalStateT (Test.Util.loadWith actualExpr) (Import.emptyStatus directoryString)
+
+        if Turtle.filename (Turtle.fromText path) == "hashFromCacheA.dhall"
+            then do
+                setCache
+                _ <- load
+                unsetCache
+            else do
+                _ <- load
+                return ()
+
         return () )
 
 failureTest :: Text -> TestTree
@@ -65,8 +80,8 @@
 
         actualExpr <- Core.throws (Parser.exprFromText mempty text)
 
-        catch
-          (do _ <- Import.load actualExpr
+        Exception.catch
+          (do _ <- Test.Util.load actualExpr
 
               fail "Import should have failed, but it succeeds")
           (\(SourcedException _ (MissingImports _)) -> pure ()) )
diff --git a/tests/Dhall/Test/Main.hs b/tests/Dhall/Test/Main.hs
--- a/tests/Dhall/Test/Main.hs
+++ b/tests/Dhall/Test/Main.hs
@@ -4,7 +4,9 @@
 import Test.Tasty      (TestTree)
 
 import qualified Dhall.Test.Dhall
+import qualified Dhall.Test.Diff
 import qualified Dhall.Test.Format
+import qualified Dhall.Test.SemanticHash
 import qualified Dhall.Test.Import
 import qualified Dhall.Test.Lint
 import qualified Dhall.Test.Normalization
@@ -13,6 +15,7 @@
 import qualified Dhall.Test.Regression
 import qualified Dhall.Test.Tutorial
 import qualified Dhall.Test.TypeCheck
+import qualified Dhall.Test.TypeInference
 import qualified GHC.IO.Encoding
 import qualified System.Directory
 import qualified System.Environment
@@ -29,18 +32,27 @@
 
     typecheckingTests <- Dhall.Test.TypeCheck.getTests
 
+    typeinferenceTests <- Dhall.Test.TypeInference.getTests
+
     importingTests <- Dhall.Test.Import.getTests
 
     lintTests <- Dhall.Test.Lint.getTests
 
+    diffTests <- Dhall.Test.Diff.getTests
+
+    semanticHashTests <- Dhall.Test.SemanticHash.getTests
+
     let testTree =
             Test.Tasty.testGroup "Dhall Tests"
                 [ normalizationTests
                 , parsingTests
                 , importingTests
                 , typecheckingTests
+                , typeinferenceTests
                 , formattingTests
                 , lintTests
+                , diffTests
+                , semanticHashTests
                 , Dhall.Test.Regression.tests
                 , Dhall.Test.Tutorial.tests
                 , Dhall.Test.QuickCheck.tests
diff --git a/tests/Dhall/Test/Normalization.hs b/tests/Dhall/Test/Normalization.hs
--- a/tests/Dhall/Test/Normalization.hs
+++ b/tests/Dhall/Test/Normalization.hs
@@ -46,11 +46,7 @@
     let unitTestFiles = do
             path <- Turtle.lstree "./dhall-lang/tests/normalization/success/unit"
 
-            let skip =
-                    [ normalizationDirectory </> "unit/EmptyAlternativeA.dhall"
-                    -- https://github.com/dhall-lang/dhall-lang/issues/505
-                    , normalizationDirectory </> "unit/OperatorTextConcatenateLhsEmptyA.dhall"
-                    ]
+            let skip = []
 
             Monad.guard (path `notElem` skip)
 
diff --git a/tests/Dhall/Test/Parser.hs b/tests/Dhall/Test/Parser.hs
--- a/tests/Dhall/Test/Parser.hs
+++ b/tests/Dhall/Test/Parser.hs
@@ -2,6 +2,7 @@
 
 module Dhall.Test.Parser where
 
+import Data.Monoid ((<>))
 import Data.Text (Text)
 import Dhall.Core (Expr, Import)
 import Dhall.TypeCheck (X)
@@ -9,11 +10,15 @@
 import Test.Tasty (TestTree)
 import Turtle (FilePath, (</>))
 
+import qualified Codec.CBOR.Read      as CBOR
+import qualified Codec.CBOR.Term      as CBOR
 import qualified Codec.Serialise      as Serialise
 import qualified Control.Monad        as Monad
+import qualified Data.ByteString      as ByteString
 import qualified Data.ByteString.Lazy as ByteString.Lazy
 import qualified Data.Text            as Text
 import qualified Data.Text.IO         as Text.IO
+import qualified Data.Text.Encoding   as Text.Encoding
 import qualified Dhall.Binary         as Binary
 import qualified Dhall.Core           as Core
 import qualified Dhall.Parser         as Parser
@@ -52,7 +57,7 @@
             let skip =
                     [ -- These two unexpected successes are due to not correctly
                       -- requiring non-empty whitespace after the `:` in a type
-                      -- annotatoin
+                      -- annotation
                       parseDirectory </> "failure/annotation.dhall"
                     , parseDirectory </> "failure/unit/ImportEnvWrongEscape.dhall"
 
@@ -77,17 +82,30 @@
     failureTests <- do
         Test.Util.discover (Turtle.chars <> ".dhall") shouldNotParse failureFiles
 
-    let binaryDecodeFiles =
+    let binaryDecodeSuccessFiles =
             Turtle.lstree (binaryDecodeDirectory </> "success")
 
-    binaryDecodeTests <- do
-        Test.Util.discover (Turtle.chars <* "A.dhallb") shouldDecode binaryDecodeFiles
+    binaryDecodeSuccessTests <- do
+        Test.Util.discover (Turtle.chars <* "A.dhallb") shouldDecode binaryDecodeSuccessFiles
 
+    let binaryDecodeFailureFiles = do
+            path <- Turtle.lstree (binaryDecodeDirectory </> "failure")
+
+            let skip = []
+
+            Monad.guard (path `notElem` skip)
+
+            return path
+
+    binaryDecodeFailureTests <- do
+        Test.Util.discover (Turtle.chars <* ".dhallb") shouldNotDecode binaryDecodeFailureFiles
+
     let testTree =
             Tasty.testGroup "parser tests"
                 [ successTests
                 , failureTests
-                , binaryDecodeTests
+                , binaryDecodeSuccessTests
+                , binaryDecodeFailureTests
                 ]
 
     return testTree
@@ -103,23 +121,33 @@
 
         expression <- Core.throws (Parser.exprFromText mempty text)
 
-        let term = Binary.encode expression
+        let term = Binary.encodeExpression expression
 
         let bytes = Serialise.serialise term
 
-        let message = "The expected CBOR representation doesn't match the actual one"
-        Tasty.HUnit.assertEqual message encoded bytes
+        Monad.unless (encoded == bytes) $ do
+            ("", expected) <- Core.throws (CBOR.deserialiseFromBytes CBOR.decodeTerm encoded)
 
+            let message = "The expected CBOR representation doesn't match the actual one\n"
+                          ++ "expected: " ++ show expected ++ "\n but got: " ++ show term
+                          ++ "\n expr: " ++ show expression
+
+            Tasty.HUnit.assertFailure message
+
+
 shouldNotParse :: Text -> TestTree
 shouldNotParse path = do
     let pathString = Text.unpack path
 
     Tasty.HUnit.testCase pathString (do
-        text <- Text.IO.readFile pathString
+        bytes <- ByteString.readFile pathString
 
-        case Parser.exprFromText mempty text of
-            Left  _ -> return ()
-            Right _ -> fail "Unexpected successful parser" )
+        case Text.Encoding.decodeUtf8' bytes of
+            Left _ -> return ()
+            Right text -> do
+                case Parser.exprFromText mempty text of
+                    Left  _ -> return ()
+                    Right _ -> fail "Unexpected successful parser" )
 
 shouldDecode :: Text -> TestTree
 shouldDecode pathText = do
@@ -143,3 +171,16 @@
                 "The decoded expression didn't match the parsed expression"
 
         Tasty.HUnit.assertEqual message decodedExpression strippedExpression )
+
+shouldNotDecode :: Text -> TestTree
+shouldNotDecode pathText = do
+    let pathString = Text.unpack pathText
+
+    Tasty.HUnit.testCase pathString (do
+        bytes <- ByteString.Lazy.readFile (pathString <> ".dhallb")
+
+        term <- Core.throws (Serialise.deserialiseOrFail bytes)
+
+        case Binary.decodeExpression term :: Either Binary.DecodingFailure (Expr X Import) of
+            Left _ -> return ()
+            Right _ -> fail "Unexpected successful decode" )
diff --git a/tests/Dhall/Test/QuickCheck.hs b/tests/Dhall/Test/QuickCheck.hs
--- a/tests/Dhall/Test/QuickCheck.hs
+++ b/tests/Dhall/Test/QuickCheck.hs
@@ -27,22 +27,27 @@
     , Var(..)
     )
 
+import Data.Functor.Identity (Identity(..))
 import Dhall.Set (Set)
 import Dhall.Src (Src(..))
+import Dhall.TypeCheck (Typer)
 import Numeric.Natural (Natural)
 import Test.QuickCheck
-    (Arbitrary(..), Gen, Positive(..), Property, genericShrink, (===), (==>))
+    (Arbitrary(..), Gen, Positive(..), Property, NonNegative(..), genericShrink, (===), (==>))
 import Test.QuickCheck.Instances ()
 import Test.Tasty (TestTree)
 import Text.Megaparsec (SourcePos(..), Pos)
 
+import qualified Control.Spoon
 import qualified Codec.Serialise
 import qualified Data.Coerce
 import qualified Data.List
-import qualified Dhall.Map
 import qualified Data.Sequence
 import qualified Dhall.Binary
+import qualified Dhall.Context
+import qualified Dhall.Core
 import qualified Dhall.Diff
+import qualified Dhall.Map
 import qualified Dhall.Set
 import qualified Dhall.TypeCheck
 import qualified Test.QuickCheck
@@ -58,6 +63,7 @@
 
 instance (Arbitrary a, Ord a) => Arbitrary (Set a) where
   arbitrary = Dhall.Set.fromList <$> arbitrary
+  shrink = map Dhall.Set.fromList . shrink . Dhall.Set.toList
 
 lift0 :: a -> Gen a
 lift0 = pure
@@ -108,13 +114,6 @@
         <*> arbitrary
         <*> arbitrary
 
-natural :: (Arbitrary a, Num a) => Gen a
-natural =
-    Test.QuickCheck.frequency
-        [ (7, arbitrary)
-        , (1, fmap (\x -> x + (2 ^ (64 :: Int))) arbitrary)
-        ]
-
 integer :: (Arbitrary a, Num a) => Gen a
 integer =
     Test.QuickCheck.frequency
@@ -184,36 +183,36 @@
                           bindings <- Test.QuickCheck.vectorOf n arbitrary
                           body     <- arbitrary
                           return (Let (binding :| bindings) body)
-                  in  ( 1, Test.QuickCheck.oneof [ letExpression ])
+                  in  ( 7, letExpression)
                 , ( 1, lift2 Annot)
-                , ( 7, lift0 Bool)
+                , ( 1, lift0 Bool)
                 , ( 7, lift1 BoolLit)
                 , ( 1, lift2 BoolAnd)
                 , ( 1, lift2 BoolOr)
                 , ( 1, lift2 BoolEQ)
                 , ( 1, lift2 BoolNE)
                 , ( 1, lift3 BoolIf)
-                , ( 7, lift0 Natural)
-                , ( 7, fmap NaturalLit natural)
-                , ( 7, lift0 NaturalFold)
-                , ( 7, lift0 NaturalBuild)
-                , ( 7, lift0 NaturalIsZero)
-                , ( 7, lift0 NaturalEven)
-                , ( 7, lift0 NaturalOdd)
-                , ( 7, lift0 NaturalToInteger)
-                , ( 7, lift0 NaturalShow)
+                , ( 1, lift0 Natural)
+                , ( 7, fmap NaturalLit arbitrary)
+                , ( 1, lift0 NaturalFold)
+                , ( 1, lift0 NaturalBuild)
+                , ( 1, lift0 NaturalIsZero)
+                , ( 1, lift0 NaturalEven)
+                , ( 1, lift0 NaturalOdd)
+                , ( 1, lift0 NaturalToInteger)
+                , ( 1, lift0 NaturalShow)
                 , ( 1, lift2 NaturalPlus)
                 , ( 1, lift2 NaturalTimes)
-                , ( 7, lift0 Integer)
+                , ( 1, lift0 Integer)
                 , ( 7, fmap IntegerLit integer)
-                , ( 7, lift0 IntegerShow)
-                , ( 7, lift0 Double)
+                , ( 1, lift0 IntegerShow)
+                , ( 1, lift0 Double)
                 , ( 7, lift1 DoubleLit)
-                , ( 7, lift0 DoubleShow)
-                , ( 7, lift0 Text)
+                , ( 1, lift0 DoubleShow)
+                , ( 1, lift0 Text)
                 , ( 1, lift1 TextLit)
                 , ( 1, lift2 TextAppend)
-                , ( 7, lift0 List)
+                , ( 1, lift0 List)
                 , let listLit = do
                           n  <- Test.QuickCheck.choose (0, 3)
                           xs <- Test.QuickCheck.vectorOf n arbitrary
@@ -222,27 +221,27 @@
 
                   in  ( 1, listLit)
                 , ( 1, lift2 ListAppend)
-                , ( 7, lift0 ListBuild)
-                , ( 7, lift0 ListFold)
-                , ( 7, lift0 ListLength)
-                , ( 7, lift0 ListHead)
-                , ( 7, lift0 ListLast)
-                , ( 7, lift0 ListIndexed)
-                , ( 7, lift0 ListReverse)
-                , ( 7, lift0 Optional)
-                , ( 1, lift2 OptionalLit)
-                , ( 7, lift0 OptionalFold)
-                , ( 7, lift0 OptionalBuild)
+                , ( 1, lift0 ListBuild)
+                , ( 1, lift0 ListFold)
+                , ( 1, lift0 ListLength)
+                , ( 1, lift0 ListHead)
+                , ( 1, lift0 ListLast)
+                , ( 1, lift0 ListIndexed)
+                , ( 1, lift0 ListReverse)
+                , ( 1, lift0 Optional)
+                , ( 1, lift0 OptionalFold)
+                , ( 1, lift0 OptionalBuild)
                 , ( 1, lift1 Record)
-                , ( 1, lift1 RecordLit)
+                , ( 7, lift1 RecordLit)
                 , ( 1, lift1 Union)
                 , ( 1, lift3 UnionLit)
                 , ( 1, lift2 Combine)
                 , ( 1, lift2 CombineTypes)
                 , ( 1, lift2 Prefer)
                 , ( 1, lift3 Merge)
-                , ( 1, lift2 Field)
-                , ( 1, lift2 Project)
+                , ( 1, lift2 ToMap)
+                , ( 7, lift2 Field)
+                , ( 7, lift2 Project)
                 , ( 7, lift1 Embed)
                 ]
             )
@@ -302,7 +301,7 @@
 -- The standard does not yet specify how to encode `as Text`, so don't test it
 -- yet
 instance Arbitrary ImportMode where
-    arbitrary = lift0 Code
+    arbitrary = Test.QuickCheck.elements [ Code, RawText, Location ]
 
     shrink = genericShrink
 
@@ -324,9 +323,9 @@
 instance Arbitrary Var where
     arbitrary =
         Test.QuickCheck.oneof
-            [ fmap (V "_") (fromIntegral <$> (natural :: Gen Int))
+            [ fmap (V "_") (getNonNegative <$> arbitrary)
             , lift1 (\t -> V t 0)
-            , lift1 V <*> (fromIntegral <$> (natural :: Gen Int))
+            , lift1 V <*> (getNonNegative <$> arbitrary)
             ]
 
     shrink = genericShrink
@@ -349,11 +348,33 @@
         -> Either DeserialiseFailureWithEq a
     wrap = Data.Coerce.coerce
 
--- isNormalizedIsConsistentWithNormalize :: Expr () Import -> Property
--- isNormalizedIsConsistentWithNormalize expression =
---         Dhall.Core.isNormalized expression
---     === (Dhall.Core.normalize expression == expression)
+everythingWellTypedNormalizes :: Expr () () -> Property
+everythingWellTypedNormalizes expression =
+        isRight (Dhall.TypeCheck.typeWithA filterOutEmbeds Dhall.Context.empty expression)
+    ==> Test.QuickCheck.total (Dhall.Core.normalize expression :: Expr () ())
+  where
+    filterOutEmbeds :: Typer a
+    filterOutEmbeds _ = Const Sort -- This could be any ill-typed expression.
 
+isNormalizedIsConsistentWithNormalize :: Expr () Import -> Property
+isNormalizedIsConsistentWithNormalize expression =
+    case maybeProp of
+        Nothing -> Test.QuickCheck.discard
+        Just prop -> prop
+  where
+      maybeProp = do
+          nf <- Control.Spoon.spoon (Dhall.Core.normalize expression)
+          isNormalized <- Control.Spoon.spoon (Dhall.Core.isNormalized expression)
+          return $ isNormalized === (nf == expression)
+
+normalizeWithMIsConsistentWithNormalize :: Expr () Import -> Property
+normalizeWithMIsConsistentWithNormalize expression =
+    case Control.Spoon.spoon (nfM, nf) of
+        Just (a, b) -> a === b
+        Nothing -> Test.QuickCheck.discard
+  where nfM = runIdentity (Dhall.Core.normalizeWithM (\_ -> Identity Nothing) expression)
+        nf = Dhall.Core.normalize expression :: Expr () Import
+
 isSameAsSelf :: Expr () Import -> Property
 isSameAsSelf expression =
   hasNoImportAndTypechecks ==> Dhall.Diff.same (Dhall.Diff.diffExpression expression expression)
@@ -369,10 +390,18 @@
         [ ( "Binary serialization should round-trip"
           , Test.QuickCheck.property binaryRoundtrip
           )
-        -- , ( "isNormalized should be consistent with normalize"
-        --   , Test.QuickCheck.property
-        --       (Test.QuickCheck.withMaxSuccess 10000 isNormalizedIsConsistentWithNormalize)
-        --   )
+        , ( "everything well-typed should normalize"
+          , Test.QuickCheck.property
+              (Test.QuickCheck.withMaxSuccess 10000 everythingWellTypedNormalizes)
+          )
+        , ( "isNormalized should be consistent with normalize"
+          , Test.QuickCheck.property
+              (Test.QuickCheck.withMaxSuccess 10000 isNormalizedIsConsistentWithNormalize)
+          )
+        , ( "normalizeWithM should be consistent with normalize"
+          , Test.QuickCheck.property
+              (Test.QuickCheck.withMaxSuccess 10000 normalizeWithMIsConsistentWithNormalize)
+          )
         , ( "An expression should have no difference with itself"
           , Test.QuickCheck.property
               (Test.QuickCheck.withMaxSuccess 10000 isSameAsSelf)
diff --git a/tests/Dhall/Test/Regression.hs b/tests/Dhall/Test/Regression.hs
--- a/tests/Dhall/Test/Regression.hs
+++ b/tests/Dhall/Test/Regression.hs
@@ -1,10 +1,12 @@
 {-# LANGUAGE DeriveAnyClass    #-}
 {-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Dhall.Test.Regression where
 
 import qualified Control.Exception
+import qualified Data.Text.IO
 import qualified Data.Text.Lazy.IO
 import qualified Data.Text.Prettyprint.Doc
 import qualified Data.Text.Prettyprint.Doc.Render.Text
@@ -20,8 +22,8 @@
 import qualified Test.Tasty
 import qualified Test.Tasty.HUnit
 
-import Dhall.Import (Imported)
-import Dhall.Parser (Src)
+import Dhall.Import (Imported, MissingImports(..))
+import Dhall.Parser (Src, SourcedException(..))
 import Dhall.TypeCheck (TypeError, X)
 import Test.Tasty (TestTree)
 import Test.Tasty.HUnit ((@?=))
@@ -36,6 +38,8 @@
         , issue201
         , issue216
         , issue253
+        , issue1131a
+        , issue1131b
         , parsing0
         , typeChecking0
         , typeChecking1
@@ -84,13 +88,18 @@
 issue126 :: TestTree
 issue126 = Test.Tasty.HUnit.testCase "Issue #126" (do
     e <- Util.code "''\nfoo\nbar\n''"
-    Util.normalize' e @?= "''\nfoo\nbar\n''" )
+    Util.normalize' e @?= "\"foo\\nbar\\n\"" )
 
 issue151 :: TestTree
 issue151 = Test.Tasty.HUnit.testCase "Issue #151" (do
     let shouldNotTypeCheck text = do
-            let handler :: Imported (TypeError Src X) -> IO Bool
-                handler _ = return True
+            let handler :: SourcedException MissingImports -> IO Bool
+                handler (SourcedException _ (MissingImports [e])) =
+                    case Control.Exception.fromException e :: Maybe (Imported (TypeError Src X)) of
+                        Just _ -> return True
+                        Nothing -> return False
+                handler _ = do
+                    return True
 
             let typeCheck = do
                     _ <- Util.code text
@@ -133,7 +142,8 @@
 issue216 :: TestTree
 issue216 = Test.Tasty.HUnit.testCase "Issue #216" (do
     -- Verify that pretty-printing preserves string interpolation
-    e <- Util.code "./tests/regression/issue216a.dhall"
+    source <- Data.Text.IO.readFile "./tests/regression/issue216b.dhall"
+    e <- Util.code source
     let doc       = Data.Text.Prettyprint.Doc.pretty e
     let docStream = Data.Text.Prettyprint.Doc.layoutSmart Dhall.Pretty.layoutOpts doc
     let text0 = Data.Text.Prettyprint.Doc.Render.Text.renderLazy docStream
@@ -152,6 +162,14 @@
     -- infinitely loop
     Just _ <- System.Timeout.timeout 1000000 (Control.Exception.evaluate $! result)
     return () )
+
+issue1131a :: TestTree
+issue1131a = Test.Tasty.HUnit.testCase "Issue #1131 a"
+    (Util.assertDoesntTypeCheck "toMap {=} : <>.x")
+
+issue1131b :: TestTree
+issue1131b = Test.Tasty.HUnit.testCase "Issue #1131 b"
+    (Util.assertDoesntTypeCheck "toMap {=} : List {mapKey : Text, mapValue : 0}")
 
 parsing0 :: TestTree
 parsing0 = Test.Tasty.HUnit.testCase "Parsing regression #0" (do
diff --git a/tests/Dhall/Test/SemanticHash.hs b/tests/Dhall/Test/SemanticHash.hs
new file mode 100644
--- /dev/null
+++ b/tests/Dhall/Test/SemanticHash.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Dhall.Test.SemanticHash where
+
+import Data.Monoid((<>))
+import Data.Text (Text)
+import Prelude hiding (FilePath)
+import Test.Tasty (TestTree)
+import Turtle (FilePath)
+
+import qualified Data.Text                             as Text
+import qualified Data.Text.IO                          as Text.IO
+import qualified Dhall.Binary                          as Binary
+import qualified Dhall.Core                            as Core
+import qualified Dhall.Import                          as Import
+import qualified Dhall.Parser                          as Parser
+import qualified Dhall.Test.Util                       as Test.Util
+import qualified Test.Tasty                            as Tasty
+import qualified Test.Tasty.HUnit                      as Tasty.HUnit
+import qualified Turtle
+
+hashDirectory :: FilePath
+hashDirectory = "./dhall-lang/tests/semantic-hash"
+
+getTests :: IO TestTree
+getTests = do
+    hashTests <- Test.Util.discover (Turtle.chars <* "A.dhall") hashTest (Turtle.lstree hashDirectory)
+
+    return (Tasty.testGroup "semantic-hash tests" [ hashTests ])
+
+hashTest :: Text -> TestTree
+hashTest prefix =
+    Tasty.HUnit.testCase (Text.unpack prefix) $ do
+        let codeFile = Test.Util.toDhallPath (prefix <> "A.dhall")
+        let hashFile = Test.Util.toDhallPath (prefix <> "B.hash")
+
+        expr <- Core.throws (Parser.exprFromText mempty codeFile)
+
+        resolved <- Import.load expr
+
+        let normalized = Core.alphaNormalize (Core.normalize resolved)
+
+        let actualHash = Import.hashExpressionToCode Binary.defaultStandardVersion normalized
+
+        expectedHash <- Text.stripEnd <$> Text.IO.readFile (Text.unpack hashFile)
+
+        let message = "The hash did not match the expected hash."
+
+        Tasty.HUnit.assertEqual message actualHash expectedHash
diff --git a/tests/Dhall/Test/TypeCheck.hs b/tests/Dhall/Test/TypeCheck.hs
--- a/tests/Dhall/Test/TypeCheck.hs
+++ b/tests/Dhall/Test/TypeCheck.hs
@@ -13,7 +13,6 @@
 import qualified Control.Monad     as Monad
 import qualified Data.Text         as Text
 import qualified Dhall.Core        as Core
-import qualified Dhall.Import      as Import
 import qualified Dhall.Parser      as Parser
 import qualified Dhall.Test.Util   as Test.Util
 import qualified Dhall.TypeCheck   as TypeCheck
@@ -31,9 +30,7 @@
     let failureTestFiles = do
             path <- Turtle.lstree (typecheckDirectory </> "failure")
 
-            let skip =
-                    [ typecheckDirectory </> "failure/duplicateFields.dhall"
-                    ]
+            let skip = []
 
             Monad.guard (path `notElem` skip)
 
@@ -60,7 +57,7 @@
 
         let annotatedExpr = Core.Annot actualExpr expectedExpr
 
-        resolvedExpr <- Import.load annotatedExpr
+        resolvedExpr <- Test.Util.load annotatedExpr
 
         _ <- Core.throws (TypeCheck.typeOf resolvedExpr)
 
@@ -75,7 +72,7 @@
 
         let io :: IO Bool
             io = do
-                _ <- Import.load expression
+                _ <- Test.Util.load expression
                 return True
 
         let handler :: SomeException -> IO Bool
diff --git a/tests/Dhall/Test/TypeInference.hs b/tests/Dhall/Test/TypeInference.hs
new file mode 100644
--- /dev/null
+++ b/tests/Dhall/Test/TypeInference.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Dhall.Test.TypeInference where
+
+import Data.Monoid (mempty, (<>))
+import Data.Text (Text)
+import Prelude hiding (FilePath)
+import Test.Tasty (TestTree)
+
+import qualified Data.Text         as Text
+import qualified Data.Text.IO      as Text.IO
+import qualified Dhall.Core        as Core
+import qualified Dhall.Import      as Import
+import qualified Dhall.Parser      as Parser
+import qualified Dhall.Test.Util   as Test.Util
+import qualified Dhall.TypeCheck   as TypeCheck
+import qualified Test.Tasty        as Tasty
+import qualified Test.Tasty.HUnit  as Tasty.HUnit
+import qualified Turtle
+
+getTests :: IO TestTree
+getTests = do
+    successTests <- Test.Util.discover (Turtle.chars <* "A.dhall") successTest (Turtle.lstree "./dhall-lang/tests/type-inference/success")
+
+    let testTree = Tasty.testGroup "type-inference tests"
+            [ successTests
+            ]
+
+    return testTree
+
+successTest :: Text -> TestTree
+successTest prefix = do
+    Tasty.HUnit.testCase (Text.unpack prefix) $ do
+        value <- expr "A.dhall"
+
+        expectedType <- expr "B.dhall"
+
+        inferredType <- Core.throws (TypeCheck.typeOf value)
+
+        let message = "The inferred type did not match the expected type"
+
+        Tasty.HUnit.assertEqual message expectedType inferredType
+  where
+    expr suffix = do
+        code <- Text.IO.readFile (Text.unpack prefix <> suffix)
+
+        e <- Core.throws (Parser.exprFromText mempty code)
+
+        Import.assertNoImports (Core.denote e)
diff --git a/tests/Dhall/Test/Util.hs b/tests/Dhall/Test/Util.hs
--- a/tests/Dhall/Test/Util.hs
+++ b/tests/Dhall/Test/Util.hs
@@ -1,24 +1,30 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE CPP #-}
 
 module Dhall.Test.Util
     ( code
     , codeWith
     , equivalent
+    , load
+    , loadWith
     , normalize'
     , normalizeWith'
     , assertNormalizesTo
     , assertNormalizesToWith
     , assertNormalized
     , assertTypeChecks
+    , assertDoesntTypeCheck
     , discover
     , toDhallPath
     ) where
 
+import Control.Monad.Trans.State.Strict (StateT)
 import Data.Bifunctor (first)
 import Data.Text (Text)
 import Dhall.Context (Context)
-import Dhall.Core (Expr, Normalizer, ReifiedNormalizer(..))
+import Dhall.Core (Expr, Normalizer, ReifiedNormalizer(..), Import)
+import Dhall.Import (Status)
 import Dhall.Parser (Src)
 import Dhall.TypeCheck (X)
 import Prelude hiding (FilePath)
@@ -35,9 +41,20 @@
 import qualified Dhall.Import
 import qualified Dhall.Parser
 import qualified Dhall.TypeCheck
+import qualified Control.Monad.Trans.State.Strict as State
 import qualified Test.Tasty        as Tasty
 import qualified Turtle
 
+#ifndef WITH_HTTP
+import Control.Monad.IO.Class (MonadIO(..))
+import Dhall.Core (URL(..))
+import Lens.Family.State.Strict (zoom)
+
+import qualified Data.Foldable
+import qualified Data.Text.Encoding
+import qualified Data.Text.IO
+#endif
+
 normalize' :: Expr Src X -> Text
 normalize' = Dhall.Core.pretty . Dhall.Core.normalize
 
@@ -53,12 +70,79 @@
     expr0 <- case Dhall.Parser.exprFromText mempty expr of
         Left parseError -> Control.Exception.throwIO parseError
         Right expr0     -> return expr0
-    expr1 <- Dhall.Import.load expr0
+    expr1 <- load expr0
     case Dhall.TypeCheck.typeWith ctx expr1 of
         Left typeError -> Control.Exception.throwIO typeError
         Right _        -> return ()
     return expr1
 
+load :: Expr Src Import -> IO (Expr Src X)
+load expression =
+    State.evalStateT (loadWith expression) (Dhall.Import.emptyStatus ".")
+
+#ifdef WITH_HTTP
+loadWith :: Expr Src Import -> StateT Status IO (Expr Src X)
+loadWith = Dhall.Import.loadWith
+
+#else
+loadWith :: Expr Src Import -> StateT Status IO (Expr Src X)
+loadWith expr = do
+    let mockRemote' url = do
+            liftIO . putStrLn $ "\nTesting without real HTTP support --"
+                ++ " using mock HTTP client to resolve remote import."
+            mockRemote url
+    zoom Dhall.Import.remote (State.put mockRemote')
+    Dhall.Import.loadWith expr
+
+mockRemote :: Dhall.Core.URL -> StateT Status IO Data.Text.Text
+-- Matches anything pointing to
+-- `https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/`
+mockRemote (URL { authority = "raw.githubusercontent.com"
+                , path = Dhall.Core.File (Dhall.Core.Directory components) file })
+  | take 3 (reverse components) == ["dhall-lang", "dhall-lang", "master"] = do
+    let dropEnd n ls = take (length ls - n) ls
+    let localDir = dropEnd 3 components ++ ["dhall-lang"]
+
+    localPath <- Dhall.Import.localToPath Dhall.Core.Here (Dhall.Core.File (Dhall.Core.Directory localDir) file)
+    liftIO $ Data.Text.IO.readFile localPath
+
+-- Matches anything pointing to
+-- `https://test.dhall-lang.org/Bool/package.dhall`; checks that a `test` header
+-- is present and redirects to the local copy of the prelude.
+mockRemote (URL { authority = "test.dhall-lang.org"
+                , path = Dhall.Core.File (Dhall.Core.Directory components) file
+                , headers = Just headersExpr }) =
+    case Data.Foldable.find ((== "test") . fst) hs of
+        Nothing -> fail $ "(mock http) Tried to load an import from "
+                          ++"\"test.dhall-lang.org\""
+                          ++ "without setting the \"test\" header field."
+        Just (_, _) -> do
+            let localDir = components ++ ["Prelude", "dhall-lang"]
+            localPath <- Dhall.Import.localToPath Dhall.Core.Here (Dhall.Core.File (Dhall.Core.Directory localDir) file)
+            liftIO $ Data.Text.IO.readFile localPath
+  where
+    hs = Dhall.Import.toHeaders headersExpr
+
+-- Emulates `https://httpbin.org/user-agent`
+mockRemote (URL { authority = "httpbin.org"
+                , path = Dhall.Core.File (Dhall.Core.Directory []) "user-agent"
+                , headers = Just headersExpr }) =
+    case Data.Foldable.find ((== "user-agent") . fst) hs of
+        Nothing -> fail $ "(mock http) Tried to read the user agent via "
+                          ++ "\"httpbin.com/user-agent\" without supplying one "
+                          ++ "in the header!"
+        Just (_, userAgent) -> do
+            let agentText = Data.Text.Encoding.decodeUtf8 userAgent
+            return ("{\n  \"user-agent\": \"" <> agentText <> "\"\n}\n")
+  where
+    hs = Dhall.Import.toHeaders headersExpr
+
+mockRemote url = do
+    let urlString = Text.unpack (Dhall.Core.pretty url)
+    fail ("(mock http) Url does not match any of the hard-coded rules: "
+        <> urlString)
+#endif
+
 equivalent :: Text -> Text -> IO ()
 equivalent text0 text1 = do
     expr0 <- fmap Dhall.Core.normalize (code text0) :: IO (Expr X X)
@@ -87,15 +171,25 @@
 assertTypeChecks :: Text -> IO ()
 assertTypeChecks text = Data.Functor.void (code text)
 
+assertDoesntTypeCheck :: Text -> IO ()
+assertDoesntTypeCheck text = do
+    expr0 <- case Dhall.Parser.exprFromText mempty text of
+        Left parseError -> Control.Exception.throwIO parseError
+        Right e         -> return e
+    expr1 <- load expr0
+    case Dhall.TypeCheck.typeOf expr1 of
+        Left _      -> return ()
+        Right type_ -> fail ("Bad type for " <> Text.unpack text <> "\n  " <> show type_)
+
 {-| Automatically run a test on all files in a directory tree that end in
     @A.dhall@
 -}
 discover :: Pattern Text -> (Text -> TestTree) -> Shell FilePath -> IO TestTree
 discover pattern buildTest paths = do
     let shell = do
-            path <- paths
+            path_ <- paths
 
-            let pathText = Turtle.format fp path
+            let pathText = Turtle.format fp path_
 
             prefix : _ <- return (Turtle.match pattern pathText)
 
diff --git a/tests/diff/appList.txt b/tests/diff/appList.txt
new file mode 100644
--- /dev/null
+++ b/tests/diff/appList.txt
@@ -0,0 +1,4 @@
+  λ(… :   …
+        → …)
+→ …@…
+  …
diff --git a/tests/diff/appListA.dhall b/tests/diff/appListA.dhall
new file mode 100644
--- /dev/null
+++ b/tests/diff/appListA.dhall
@@ -0,0 +1,1 @@
+λ(f : List Bool -> Bool) → f ([] : List Bool)
diff --git a/tests/diff/appListB.dhall b/tests/diff/appListB.dhall
new file mode 100644
--- /dev/null
+++ b/tests/diff/appListB.dhall
@@ -0,0 +1,1 @@
+λ(f : List Bool -> Bool) → f ([] : List Bool)
diff --git a/tests/format/escapeSingleQuotedOpenInterpolationB.dhall b/tests/format/escapeSingleQuotedOpenInterpolationB.dhall
--- a/tests/format/escapeSingleQuotedOpenInterpolationB.dhall
+++ b/tests/format/escapeSingleQuotedOpenInterpolationB.dhall
@@ -1,3 +1,1 @@
-''
-''${
-''
+"\${\n"
diff --git a/tests/format/innerMultilineA.dhall b/tests/format/innerMultilineA.dhall
new file mode 100644
--- /dev/null
+++ b/tests/format/innerMultilineA.dhall
@@ -0,0 +1,11 @@
+{ inner =
+  ''
+
+  one
+
+  two
+
+  three
+
+  ''
+}
diff --git a/tests/format/innerMultilineB.dhall b/tests/format/innerMultilineB.dhall
new file mode 100644
--- /dev/null
+++ b/tests/format/innerMultilineB.dhall
@@ -0,0 +1,1 @@
+{ inner = "\none\n\ntwo\n\nthree\n\n" }
diff --git a/tests/format/multilineB.dhall b/tests/format/multilineB.dhall
--- a/tests/format/multilineB.dhall
+++ b/tests/format/multilineB.dhall
@@ -1,3 +1,1 @@
-''
-ABC
-DEF''
+"ABC\nDEF"
diff --git a/tests/format/unicodeA.dhall b/tests/format/unicodeA.dhall
--- a/tests/format/unicodeA.dhall
+++ b/tests/format/unicodeA.dhall
@@ -1,15 +1,15 @@
   λ(isActive : Bool)
 →   { barLeftEnd =
-        [ "┨" ] : Optional Text
+        Some "┨"
     , barRightEnd =
-        [ "┠" ] : Optional Text
+        Some "┠"
     , separator =
-        [ "┃" ] : Optional Text
+        Some "┃"
     , alignment =
           < ToTheLeft = {=} | ToTheRight : {} | Centered : {} >
         : ./Alignment.dhall
     , barWidth =
-        [] : Optional Natural
+        None Natural
     , barSegments =
         [ "index", "command", "path", "title" ]
     }
diff --git a/tests/format/unicodeB.dhall b/tests/format/unicodeB.dhall
--- a/tests/format/unicodeB.dhall
+++ b/tests/format/unicodeB.dhall
@@ -1,15 +1,15 @@
   λ(isActive : Bool)
 →   { barLeftEnd =
-        [ "┨" ] : Optional Text
+        Some "┨"
     , barRightEnd =
-        [ "┠" ] : Optional Text
+        Some "┠"
     , separator =
-        [ "┃" ] : Optional Text
+        Some "┃"
     , alignment =
           < ToTheLeft = {=} | ToTheRight : {} | Centered : {} >
         : ./Alignment.dhall
     , barWidth =
-        [] : Optional Natural
+        None Natural
     , barSegments =
         [ "index", "command", "path", "title" ]
     }
diff --git a/tests/regression/issue151a.dhall b/tests/regression/issue151a.dhall
--- a/tests/regression/issue151a.dhall
+++ b/tests/regression/issue151a.dhall
@@ -1,1 +1,1 @@
-let foo : (\(x : A) -> x x) (\(x : A) -> x x) = 1 in foo
+\(A : Type) -> let foo : (\(x : A) -> x x) (\(x : A) -> x x) = 1 in foo
diff --git a/tests/regression/issue151b.dhall b/tests/regression/issue151b.dhall
--- a/tests/regression/issue151b.dhall
+++ b/tests/regression/issue151b.dhall
@@ -1,1 +1,1 @@
-\(omega : ((\(x : A) -> x x) (\(x : A) -> x x))) -> omega 1
+\(A : Type) -> \(omega : ((\(x : A) -> x x) (\(x : A) -> x x))) -> omega 1
