packages feed

dhall-nix (empty) → 1.0.0

raw patch · 5 files changed

+571/−0 lines, 5 filesdep +basedep +containersdep +data-fixsetup-changed

Dependencies added: base, containers, data-fix, dhall, dhall-nix, hnix, neat-interpolation, optparse-generic, text, trifecta, vector

Files

+ LICENSE view
@@ -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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dhall-nix.cabal view
@@ -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
+ exec/Main.hs view
@@ -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
+ src/Dhall/Nix.hs view
@@ -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