diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2017 Gabriel Gonzalez
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Gabriel Gonzalez nor the names of other contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/dhall-nix.cabal b/dhall-nix.cabal
new file mode 100644
--- /dev/null
+++ b/dhall-nix.cabal
@@ -0,0 +1,53 @@
+Name: dhall-nix
+Version: 1.0.0
+Cabal-Version: >=1.8.0.2
+Build-Type: Simple
+Tested-With: GHC == 8.0.1
+License: BSD3
+License-File: LICENSE
+Copyright: 2017 Gabriel Gonzalez
+Author: Gabriel Gonzalez
+Maintainer: Gabriel439@gmail.com
+Bug-Reports: https://github.com/Gabriel439/Haskell-Dhall-Nix-Library/issues
+Synopsis: Dhall to Nix compiler
+Description:
+  Use this package if you want to compile Dhall expressions to the Nix language.
+  You can use this package as a library or an executable:
+  .
+  * See the "Dhall.Nix" module if you want to use this package as a library
+  .
+  * Use the @dhall-to-nix@ program from this package if you want an executable
+  .
+  The "Dhall.Nix" module also contains instructions for how to use this package
+Category: Compiler
+Source-Repository head
+    Type: git
+    Location: https://github.com/Gabriel439/Haskell-Dhall-Nix-Library
+
+Library
+    Hs-Source-Dirs: src
+    Build-Depends:
+        base               >= 4.8.0.0  && < 5   ,
+        containers                        < 0.6 ,
+        data-fix                          < 0.1 ,
+        dhall              >= 1.0.1    && < 1.1 ,
+        hnix               >= 0.3.4    && < 0.4 ,
+        neat-interpolation                < 0.4 ,
+        text               >= 0.8.0.0  && < 1.3 ,
+        vector             >= 0.3      && < 0.13
+    Exposed-Modules:
+        Dhall.Nix
+    GHC-Options: -Wall
+
+Executable dhall-to-nix
+    Hs-Source-Dirs: exec
+    Main-Is: Main.hs
+    Build-Depends:
+        base             >= 4       && < 5  ,
+        dhall            >= 1.0.1   && < 1.1,
+        dhall-nix                           ,
+        hnix             >= 0.3.4   && < 0.4,
+        optparse-generic >= 1.1.1   && < 1.2,
+        text             >= 0.8.0.0 && < 1.3,
+        trifecta         >= 1.0     && < 1.7
+    GHC-Options: -Wall
diff --git a/exec/Main.hs b/exec/Main.hs
new file mode 100644
--- /dev/null
+++ b/exec/Main.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+
+module Main where
+
+import Control.Exception (SomeException)
+import Text.Trifecta.Delta (Delta(..))
+
+import qualified Control.Exception
+import qualified Data.Text.Lazy.IO
+import qualified Dhall
+import qualified Dhall.Import
+import qualified Dhall.Nix
+import qualified Dhall.Parser
+import qualified Dhall.TypeCheck
+import qualified GHC.IO.Encoding
+import qualified Nix.Pretty
+import qualified Options.Generic
+import qualified System.Exit
+import qualified System.IO
+
+main :: IO ()
+main = handle (Dhall.detailed (do
+    GHC.IO.Encoding.setLocaleEncoding GHC.IO.Encoding.utf8
+    () <- Options.Generic.getRecord "Compile Dhall to Nix"
+
+    inText <- Data.Text.Lazy.IO.getContents
+
+    expr <- case Dhall.Parser.exprFromText (Directed "(stdin)" 0 0 0 0) inText of
+        Left  err  -> Control.Exception.throwIO err
+        Right expr -> return expr
+
+    expr' <- Dhall.Import.load expr
+    case Dhall.TypeCheck.typeOf expr' of
+        Left  err -> Control.Exception.throwIO err
+        Right _   -> return ()
+
+    nix <- case Dhall.Nix.dhallToNix expr' of
+        Left err  -> Control.Exception.throwIO err
+        Right nix -> return nix
+    print (Nix.Pretty.prettyNix nix) ))
+
+handle :: IO a -> IO a
+handle = Control.Exception.handle handler
+  where
+    handler :: SomeException -> IO a
+    handler e = do
+        System.IO.hPutStrLn System.IO.stderr ""
+        System.IO.hPrint    System.IO.stderr e
+        System.Exit.exitFailure
diff --git a/src/Dhall/Nix.hs b/src/Dhall/Nix.hs
new file mode 100644
--- /dev/null
+++ b/src/Dhall/Nix.hs
@@ -0,0 +1,442 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE QuasiQuotes        #-}
+
+{-| This library only exports a single `dhallToNix` function for translating a
+    Dhall syntax tree to a Nix syntax tree for the @hnix@ library
+
+    See the @dhall@ package if you would like to transform Dhall source code
+    into a Dhall syntax tree.  Similarly, see the @hnix@ package if you would
+    like to translate a Nix syntax tree into Nix source code.
+
+    This package also provides a @dhall-to-nix@ executable which you can use to
+    compile Dhall source code directly to Nix source code for your convenience.
+
+    Any Dhall expression can be converted into an equivalent Nix expression.
+    For example, Dhall records can be converted into Nix records:
+
+> $ dhall-to-nix <<< "{ foo = 1, bar = True }"
+> { bar = true; foo = 1; }
+
+    ... and you can also convert Dhall functions to Nix functions, too:
+
+> $ dhall-to-nix <<< "λ(x : Bool) → x == False"
+> x: x == false
+
+    Many Dhall expressions have a straightforward translation to Nix expressions
+    but there are some translations that are not as obvious.  The following
+    section documents these trickier conversions:
+
+    First, all Dhall types translate to an empty record:
+
+> $ dhall-to-nix <<< "Integer"
+> {}
+
+    Polymorphic Dhall functions translate to Nix functions that ignore their
+    type argument:
+
+> $ dhall-to-nix <<< "List/head"
+> t: xs: if xs == []
+>       then null
+>       else builtins.head xs
+
+    `Optional` values translate to @null@ if missing or the unwrapped value if
+    present:
+
+> $ dhall-to-nix <<< "[] : Optional Integer"
+> null
+
+> $ dhall-to-nix <<< "[1] : Optional Integer"
+> 1
+
+    Unions are Church-encoded:
+
+> $ dhall-to-nix <<< "< Left = True | Right : Natural >"
+> { Left, Right }: Left true
+
+    `Dhall.Core.Double`s cannot be translated to Nix at all since Nix does not
+    support floating point values.
+
+    Also, all Dhall expressions are normalized before translation to Nix:
+
+> $ dhall-to-nix <<< "True == False"
+> false
+
+    You can use the @dhall-to-nix@ executable within Nix to assemble Nix
+    expressions from Dhall expressions using the following @dhallToNix@ utility
+    function:
+
+> dhallToNix = code :
+>   let
+>     file = builtins.toFile "dhall-expr" code;
+>
+>     drv = pkgs.stdenv.mkDerivation {
+>       name = "dhall-expr-as-nix";
+>
+>       buildCommand = ''
+>         dhall-to-nix <<< "${file}" > $out
+>       '';
+>
+>       buildInputs = [ pkgs.haskellPackages.dhall-nix ];
+>     };
+>   in
+>     import "${drv}";
+-}
+
+module Dhall.Nix (
+    -- * Dhall to Nix
+      dhallToNix
+
+    -- * Exceptions
+    , CompileError(..)
+    ) where
+
+import Control.Exception (Exception)
+import Data.Foldable (toList)
+import Data.Fix (Fix(..))
+import Data.Typeable (Typeable)
+import Dhall.Core (Expr(..), Var(..))
+import Dhall.TypeCheck (X(..))
+import Nix.Atoms (NAtom(..))
+import Nix.Expr
+    ( Antiquoted(..)
+    , Binding(..)
+    , NBinaryOp(..)
+    , NExprF(..)
+    , NKeyName(..)
+    , NString(..)
+    , Params(..)
+    , ParamSet(..)
+    )
+
+import qualified Data.Map
+import qualified Data.Text
+import qualified Data.Text.Lazy
+import qualified Data.Text.Lazy.Builder
+import qualified Data.Vector
+import qualified Dhall.Core
+import qualified NeatInterpolation
+
+{-| This is the exception type for all possible errors that might arise when
+    translating the Dhall syntax tree to the Nix syntax tree
+-}
+data CompileError
+    = CannotReferenceShadowedVariable Var
+    -- ^ Nix does not provide a way to reference a shadowed variable
+    | NoDoubles Double
+    -- ^ Nix does not provide a way to represent floating point values
+    deriving (Typeable)
+
+instance Show CompileError where
+    show (CannotReferenceShadowedVariable v) =
+        Data.Text.unpack [NeatInterpolation.text|
+$_ERROR: Cannot reference shadowed variable
+
+Explanation: Whenever you introduce two variables of the same name, the latter
+variable takes precedence:
+
+
+                                  This ❰x❱ ...
+                                  ⇩
+    ┌───────────────────────────────┐
+    │ λ(x : Text) → λ(x : Text) → x │
+    └───────────────────────────────┘
+                      ⇧
+                      ... refers to this ❰x❱
+
+
+The former variable is "shadowed":
+
+
+    ┌───────────────────────────────┐
+    │ λ(x : Text) → λ(x : Text) → x │
+    └───────────────────────────────┘
+        ⇧
+        This ❰x❱ is shadowed
+
+
+... and Dhall lets you reference shadowed variables using the ❰@❱ notation:
+
+
+                                  This ❰x❱ ...
+                                  ⇩
+    ┌─────────────────────────────────┐
+    │ λ(x : Text) → λ(x : Text) → x@1 │
+    └─────────────────────────────────┘
+        ⇧
+        ... now refers to this ❰x❱
+
+
+However, the Nix language does not let you reference shadowed variables and
+there is nothing analogous to ❰@❱ in Nix
+
+Your code contains the following expression:
+
+↳ $txt
+
+... which references a shadowed variable and therefore cannot be translated to
+Nix
+|]
+      where
+        txt = Data.Text.Lazy.toStrict (Dhall.Core.pretty v)
+    show (NoDoubles n) =
+        Data.Text.unpack [NeatInterpolation.text|
+$_ERROR: No doubles
+
+Explanation: Dhall values of type ❰Double❱ cannot be converted to Nix
+expressions because the Nix language provides no way to represent floating point
+values
+
+You provided the following value:
+
+↳ $txt
+
+... which cannot be translated to Nix
+|]
+      where
+        txt = Data.Text.pack (show n)
+
+_ERROR :: Data.Text.Text
+_ERROR = "\ESC[1;31mError\ESC[0m"
+
+instance Exception CompileError
+
+{-| Convert a Dhall expression to the equivalent Nix expression
+
+>>> :set -XOverloadedStrings
+>>> dhallToNix (Lam "x" Natural (Lam "y" Natural (NaturalPlus "x" "y"))) 
+Right (NAbs (Param "x") (NAbs (Param "y") (NBinary NPlus (NSym "x") (NSym "y"))))
+>>> fmap Nix.Pretty.prettyNix it
+Right x: y: x + y
+
+-}
+dhallToNix :: Expr s X -> Either CompileError (Fix NExprF)
+dhallToNix e = loop (Dhall.Core.normalize e)
+  where
+    loop (Const _) = return (Fix (NSet []))
+    loop (Var (V a 0)) = return (Fix (NSym (Data.Text.Lazy.toStrict a)))
+    loop (Var  a     ) = Left (CannotReferenceShadowedVariable a)
+    loop (Lam a _ c) = do
+        let a' = Data.Text.Lazy.toStrict a
+        c' <- loop c
+        return (Fix (NAbs (Param a') c'))
+    loop (Pi _ _ _) = return (Fix (NSet []))
+    loop (App a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NApp a' b'))
+    loop (Let a _ c d) = do
+        let a' = Data.Text.Lazy.toStrict a
+        c' <- loop c
+        d' <- loop d
+        return (Fix (NLet [NamedVar [StaticKey a'] c'] d'))
+    loop (Annot a _) = loop a
+    loop Bool = return (Fix (NSet []))
+    loop (BoolLit b) = return (Fix (NConstant (NBool b)))
+    loop (BoolAnd a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NBinary NAnd a' b'))
+    loop (BoolOr a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NBinary NOr a' b'))
+    loop (BoolEQ a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NBinary NEq a' b'))
+    loop (BoolNE a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NBinary NNEq a' b'))
+    loop (BoolIf a b c) = do
+        a' <- loop a
+        b' <- loop b
+        c' <- loop c
+        return (Fix (NIf a' b' c'))
+    loop Natural = return (Fix (NSet []))
+    loop (NaturalLit n) = return (Fix (NConstant (NInt (fromIntegral n))))
+    loop NaturalFold = do
+        let e0 = Fix (NBinary NMinus "n" (Fix (NConstant (NInt 1))))
+        let e1 = Fix (NApp (Fix (NApp (Fix (NApp "naturalFold" e0)) "t")) "succ")
+        let e2 = Fix (NApp "succ" (Fix (NApp e1 "zero")))
+        let e3 = Fix (NBinary NLte "n" (Fix (NConstant (NInt 0))))
+        let e4 = Fix (NAbs "succ" (Fix (NAbs "zero" (Fix (NIf e3 "zero" e2)))))
+        let e5 = Fix (NAbs "n" (Fix (NAbs "t" e4)))
+        return (Fix (NLet [NamedVar ["naturalFold"] e5] "naturalFold"))
+    loop NaturalBuild = do
+        let e0 = Fix (NBinary NPlus "n" (Fix (NConstant (NInt 1))))
+        let e1 = Fix (NApp (Fix (NApp "k" (Fix (NSet [])))) (Fix (NAbs "n" e0)))
+        return (Fix (NAbs "k" (Fix (NApp e1 (Fix (NConstant (NInt 0)))))))
+    loop NaturalIsZero = do
+        let e0 = Fix (NBinary NEq "n" (Fix (NConstant (NInt 0))))
+        return (Fix (NAbs "n" e0))
+    loop NaturalEven = do
+        let e0 = Fix (NBinary NMinus "n" (Fix (NConstant (NInt 2))))
+        let e1 = Fix (NApp "naturalEven" e0)
+        let e2 = Fix (NBinary NNEq "n" (Fix (NConstant (NInt 1))))
+        let e3 = Fix (NBinary NEq "n" (Fix (NConstant (NInt 0))))
+        let e4 = Fix (NBinary NOr e3 (Fix (NBinary NAnd e2 e1)))
+        let e5 = NamedVar ["naturalEven"] (Fix (NAbs "n" e4))
+        let e6 = Fix (NBinary NMinus (Fix (NConstant (NInt 0))) "n")
+        let e7 = Fix (NBinary NLte "n" (Fix (NConstant (NInt 0))))
+        let e8 = Fix (NAbs "n" (Fix (NApp "naturalEven" (Fix (NIf e7 e6 "n")))))
+        return (Fix (NLet [e5] e8))
+    loop NaturalOdd = do
+        let e0 = Fix (NBinary NMinus "n" (Fix (NConstant (NInt 2))))
+        let e1 = Fix (NApp "naturalOdd" e0)
+        let e2 = Fix (NBinary NNEq "n" (Fix (NConstant (NInt 0))))
+        let e3 = Fix (NBinary NEq "n" (Fix (NConstant (NInt 1))))
+        let e4 = Fix (NBinary NOr e3 (Fix (NBinary NAnd e2 e1)))
+        let e5 = NamedVar ["naturalOdd"] (Fix (NAbs "n" e4))
+        let e6 = Fix (NBinary NMinus (Fix (NConstant (NInt 0))) "n")
+        let e7 = Fix (NBinary NLte "n" (Fix (NConstant (NInt 0))))
+        let e8 = Fix (NAbs "n" (Fix (NApp "naturalOdd" (Fix (NIf e7 e6 "n")))))
+        return (Fix (NLet [e5] e8))
+    loop (NaturalPlus a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NBinary NPlus a' b'))
+    loop (NaturalTimes a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NBinary NMult a' b'))
+    loop Integer = return (Fix (NSet []))
+    loop (IntegerLit n) = return (Fix (NConstant (NInt (fromIntegral n))))
+    loop Double = return (Fix (NSet []))
+    loop (DoubleLit n) = Left (NoDoubles n)
+    loop Text = return (Fix (NSet []))
+    loop (TextLit a) = do
+        let a' = Data.Text.Lazy.toStrict (Data.Text.Lazy.Builder.toLazyText a)
+        return (Fix (NStr (DoubleQuoted [Plain a'])))
+    loop (TextAppend a b) = do
+        a' <- loop a
+        b' <- loop b
+        return (Fix (NBinary NPlus a' b'))
+    loop List = return (Fix (NAbs "t" (Fix (NSet []))))
+    loop (ListLit _ bs) = do
+        bs' <- mapM loop (toList bs)
+        return (Fix (NList bs'))
+    loop ListBuild = do
+        let e0 = Fix (NApp "k" (Fix (NSet [])))
+        let e1 = Fix (NBinary NConcat (Fix (NList ["x"])) "xs")
+        let e2 = Fix (NApp e0 (Fix (NAbs "x" (Fix (NAbs "xs" e1)))))
+        let e3 = Fix (NAbs "k" (Fix (NApp e2 (Fix (NList [])))))
+        return (Fix (NAbs "t" e3))
+    loop ListFold = do
+        let e0 = Fix (NApp "f" (Fix (NApp (Fix (NApp "cons" "y")) "ys")))
+        let e1 = Fix (NAbs "f" (Fix (NAbs "y" (Fix (NAbs "ys" e0)))))
+        let e2 = Fix (NApp "builtins.foldl'" e1)
+        let e3 = Fix (NApp (Fix (NApp e2 (Fix (NAbs "ys" "ys")))) "xs")
+        let e4 = Fix (NAbs "xs" (Fix (NAbs "t" (Fix (NAbs "cons" e3)))))
+        return (Fix (NAbs "t" e4))
+    loop ListLength = return (Fix (NAbs "t" "builtins.length"))
+    loop ListHead = do
+        let e0 = Fix (NApp "builtins.head" "xs")
+        let e1 = Fix (NBinary NEq "xs" (Fix (NList [])))
+        let e2 = Fix (NAbs "xs" (Fix (NIf e1 (Fix (NConstant NNull)) e0)))
+        return (Fix (NAbs "t" e2))
+    loop ListLast = do
+        let e0 = Fix (NApp "builtins.length" "xs")
+        let e1 = Fix (NBinary NMinus e0 (Fix (NConstant (NInt 1))))
+        let e2 = Fix (NApp (Fix (NApp "builtins.elemAt" "xs")) e1)
+        let e3 = Fix (NBinary NEq "xs" (Fix (NList [])))
+        let e4 = Fix (NAbs "xs" (Fix (NIf e3 (Fix (NConstant NNull)) e2)))
+        return (Fix (NAbs "t" e4))
+    loop ListIndexed = do
+        let e0 = Fix (NApp "builtins.length" "xs")
+        let e1 = Fix (NApp (Fix (NApp "builtins.elemAt" "xs")) "i")
+        let e2 =
+                [ NamedVar ["index"] "i"
+                , NamedVar ["value"] e1
+                ]
+        let e3 = Fix (NApp "builtins.genList" (Fix (NAbs "i" (Fix (NSet e2)))))
+        return (Fix (NAbs "t" (Fix (NAbs "xs" (Fix (NApp e3 e0))))))
+    loop ListReverse = do
+        let e0 = Fix (NBinary NMinus "n" "i")
+        let e1 = Fix (NBinary NMinus e0 (Fix (NConstant (NInt 1))))
+        let e2 = Fix (NApp (Fix (NApp "builtins.elemAt" "xs")) e1)
+        let e3 = Fix (NApp "builtins.genList" (Fix (NAbs "i" e2)))
+        let e4 = Fix (NApp e3 "n")
+        let e5 = Fix (NApp "builtins.length" "xs")
+        let e6 = Fix (NAbs "xs" (Fix (NLet [NamedVar ["n"] e5] e4)))
+        return (Fix (NAbs "t" e6))
+    loop Optional = return (Fix (NAbs "t" (Fix (NSet []))))
+    loop (OptionalLit _ b) =
+        if Data.Vector.null b
+            then return (Fix (NConstant NNull))
+            else dhallToNix (Data.Vector.head b)
+    loop OptionalFold = do
+        let e0 = Fix (NBinary NEq "x" (Fix (NConstant NNull)))
+        let e1 = Fix (NIf e0 "nothing" (Fix (NApp "just" "x")))
+        let e2 = Fix (NAbs "t" (Fix (NAbs "just" (Fix (NAbs "nothing" e1)))))
+        return (Fix (NAbs "t" (Fix (NAbs "x" e2))))
+    loop (Record _) = return (Fix (NSet []))
+    loop (RecordLit a) = do
+        a' <- traverse dhallToNix a
+        let a'' = do
+                (k, v) <- Data.Map.toAscList a'
+                let k' = Data.Text.Lazy.toStrict k
+                return (NamedVar [StaticKey k'] v)
+        return (Fix (NSet a''))
+    loop (Union _) = return (Fix (NSet []))
+    loop (UnionLit k v kts) = do
+        v' <- dhallToNix v
+        let k'   = Data.Text.Lazy.toStrict k
+        let e0   = do
+                k'' <- k : toList (Data.Map.keysSet kts)
+                return (Data.Text.Lazy.toStrict k'', Nothing)
+        let e1   = Data.Map.fromAscList e0
+        let e2   = Fix (NApp (Fix (NSym k')) v')
+        return (Fix (NAbs (ParamSet (FixedParamSet e1) Nothing) e2))
+    loop (Combine a b) = do
+        a' <- dhallToNix a
+        b' <- dhallToNix b
+        let e0 = Fix (NApp (Fix (NApp "map" "toKeyVals")) "ks")
+        let e1 = Fix (NApp "builtins.concatLists" e0)
+        let e2 = Fix (NApp "builtins.listToAttrs" e1)
+
+        let defL = Fix (NApp (Fix (NApp "builtins.hasAttr" "k")) "kvsL")
+        let defR = Fix (NApp (Fix (NApp "builtins.hasAttr" "k")) "kvsR")
+        let valL = Fix (NApp (Fix (NApp "builtins.getAttr" "k")) "kvsL")
+        let valR = Fix (NApp (Fix (NApp "builtins.getAttr" "k")) "kvsR")
+
+        let empty = Fix (NList [])
+        let toNameValue v =
+                let bindings =
+                        [ NamedVar ["name" ] "k"
+                        , NamedVar ["value"] v
+                        ]
+                in  Fix (NList [Fix (NSet bindings)])
+
+        let e3 = Fix (NApp (Fix (NApp "combine" valL)) valR)
+        let e4 = Fix (NApp "builtins.isAttrs" valL)
+        let e5 = Fix (NApp "builtins.isAttrs" valR)
+        let e6 = Fix (NBinary NAnd e4 e5)
+        let e7 = Fix (NIf e6 (toNameValue e3) (toNameValue valL))
+        let e8 = Fix (NIf defR e7 (toNameValue valL))
+        let e9 = Fix (NIf defR (toNameValue valR) empty)
+        let toKeyVals = Fix (NAbs "k" (Fix (NIf defL e8 e9)))
+
+        let ksL = Fix (NApp "builtins.attrNames" "kvsL")
+        let ksR = Fix (NApp "builtins.attrNames" "kvsR")
+        let ks  = Fix (NBinary NConcat ksL ksR)
+
+        let e10 =
+                [ NamedVar ["ks"       ] ks
+                , NamedVar ["toKeyVals"] toKeyVals
+                ]
+        let combine = Fix (NAbs "kvsL" (Fix (NAbs "kvsR" (Fix (NLet e10 e2)))))
+
+        let e11 = Fix (NApp (Fix (NApp "combine" a')) b')
+        return (Fix (NLet [NamedVar ["combine"] combine] e11))
+    loop (Merge a b _) = do
+        a' <- dhallToNix a
+        b' <- dhallToNix b
+        return (Fix (NApp b' a'))
+    loop (Field a b) = do
+        a' <- dhallToNix a
+        let b' = Data.Text.Lazy.toStrict b
+        return (Fix (NSelect a' [StaticKey b'] Nothing))
+    loop (Note _ b) = loop b
+    loop (Embed (X x)) = x
