1# Dhall {#sec-language-dhall} 2 3The Nixpkgs support for Dhall assumes some familiarity with Dhall's language 4support for importing Dhall expressions, which is documented here: 5 6* [`dhall-lang.org` - Installing packages](https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages) 7 8## Remote imports {#ssec-dhall-remote-imports} 9 10Nixpkgs bypasses Dhall's support for remote imports using Dhall's 11semantic integrity checks. Specifically, any Dhall import can be protected by 12an integrity check like: 13 14```dhall 15https://prelude.dhall-lang.org/v20.1.0/package.dhall 16 sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 17``` 18 19… and if the import is cached then the interpreter will load the import from 20cache instead of fetching the URL. 21 22Nixpkgs uses this trick to add all of a Dhall expression's dependencies into the 23cache so that the Dhall interpreter never needs to resolve any remote URLs. In 24fact, Nixpkgs uses a Dhall interpreter with remote imports disabled when 25packaging Dhall expressions to enforce that the interpreter never resolves a 26remote import. This means that Nixpkgs only supports building Dhall expressions 27if all of their remote imports are protected by semantic integrity checks. 28 29Instead of remote imports, Nixpkgs uses Nix to fetch remote Dhall code. For 30example, the Prelude Dhall package uses `pkgs.fetchFromGitHub` to fetch the 31`dhall-lang` repository containing the Prelude. Relying exclusively on Nix 32to fetch Dhall code ensures that Dhall packages built using Nix remain pure and 33also behave well when built within a sandbox. 34 35## Packaging a Dhall expression from scratch {#ssec-dhall-packaging-expression} 36 37We can illustrate how Nixpkgs integrates Dhall by beginning from the following 38trivial Dhall expression with one dependency (the Prelude): 39 40```dhall 41-- ./true.dhall 42 43let Prelude = https://prelude.dhall-lang.org/v20.1.0/package.dhall 44 45in Prelude.Bool.not False 46``` 47 48As written, this expression cannot be built using Nixpkgs because the 49expression does not protect the Prelude import with a semantic integrity 50check, so the first step is to freeze the expression using `dhall freeze`, 51like this: 52 53```ShellSession 54$ dhall freeze --inplace ./true.dhall 55``` 56 57… which gives us: 58 59```dhall 60-- ./true.dhall 61 62let Prelude = 63 https://prelude.dhall-lang.org/v20.1.0/package.dhall 64 sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 65 66in Prelude.Bool.not False 67``` 68 69To package that expression, we create a `./true.nix` file containing the 70following specification for the Dhall package: 71 72```nix 73# ./true.nix 74 75{ buildDhallPackage, Prelude }: 76 77buildDhallPackage { 78 name = "true"; 79 code = ./true.dhall; 80 dependencies = [ Prelude ]; 81 source = true; 82} 83``` 84 85… and we complete the build by incorporating that Dhall package into the 86`pkgs.dhallPackages` hierarchy using an overlay, like this: 87 88```nix 89# ./example.nix 90 91let 92 nixpkgs = builtins.fetchTarball { 93 url = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz"; 94 hash = "sha256-B4Q3c6IvTLg3Q92qYa8y+i4uTaphtFdjp+Ir3QQjdN0="; 95 }; 96 97 dhallOverlay = self: super: { 98 true = self.callPackage ./true.nix { }; 99 }; 100 101 overlay = self: super: { 102 dhallPackages = super.dhallPackages.override (old: { 103 overrides = 104 self.lib.composeExtensions (old.overrides or (_: _: {})) dhallOverlay; 105 }); 106 }; 107 108 pkgs = import nixpkgs { config = {}; overlays = [ overlay ]; }; 109 110in 111 pkgs 112``` 113 114… which we can then build using this command: 115 116```ShellSession 117$ nix build --file ./example.nix dhallPackages.true 118``` 119 120## Contents of a Dhall package {#ssec-dhall-package-contents} 121 122The above package produces the following directory tree: 123 124```ShellSession 125$ tree -a ./result 126result 127├── .cache 128│   └── dhall 129│   └── 122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 130├── binary.dhall 131└── source.dhall 132``` 133 134… where: 135 136* `source.dhall` contains the result of interpreting our Dhall package: 137 138 ```ShellSession 139 $ cat ./result/source.dhall 140 True 141 ``` 142 143* The `.cache` subdirectory contains one binary cache product encoding the 144 same result as `source.dhall`: 145 146 ```ShellSession 147 $ dhall decode < ./result/.cache/dhall/122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 148 True 149 ``` 150 151* `binary.dhall` contains a Dhall expression which handles fetching and decoding 152 the same cache product: 153 154 ```ShellSession 155 $ cat ./result/binary.dhall 156 missing sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 157 $ cp -r ./result/.cache .cache 158 159 $ chmod -R u+w .cache 160 161 $ XDG_CACHE_HOME=.cache dhall --file ./result/binary.dhall 162 True 163 ``` 164 165The `source.dhall` file is only present for packages that specify 166`source = true;`. By default, Dhall packages omit the `source.dhall` in order 167to conserve disk space when they are used exclusively as dependencies. For 168example, if we build the Prelude package it will only contain the binary 169encoding of the expression: 170 171```ShellSession 172$ nix build --file ./example.nix dhallPackages.Prelude 173 174$ tree -a result 175result 176├── .cache 177│   └── dhall 178│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 179└── binary.dhall 180 1812 directories, 2 files 182``` 183 184Typically, you only specify `source = true;` for the top-level Dhall expression 185of interest (such as our example `true.nix` Dhall package). However, if you 186wish to specify `source = true` for all Dhall packages, then you can amend the 187Dhall overlay like this: 188 189```nix 190 dhallOverrides = self: super: { 191 # Enable source for all Dhall packages 192 buildDhallPackage = 193 args: super.buildDhallPackage (args // { source = true; }); 194 195 true = self.callPackage ./true.nix { }; 196 }; 197``` 198 199… and now the Prelude will contain the fully decoded result of interpreting 200the Prelude: 201 202```ShellSession 203$ nix build --file ./example.nix dhallPackages.Prelude 204 205$ tree -a result 206result 207├── .cache 208│   └── dhall 209│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 210├── binary.dhall 211└── source.dhall 212 213$ cat ./result/source.dhall 214{ Bool = 215 { and = 216 \(_ : List Bool) -> 217 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 && _) True 218 , build = \(_ : Type -> _ -> _@1 -> _@2) -> _ Bool True False 219 , even = 220 \(_ : List Bool) -> 221 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 == _) True 222 , fold = 223 \(_ : Bool) -> 224225``` 226 227## Packaging functions {#ssec-dhall-packaging-functions} 228 229We already saw an example of using `buildDhallPackage` to create a Dhall 230package from a single file, but most Dhall packages consist of more than one 231file and there are two derived utilities that you may find more useful when 232packaging multiple files: 233 234* `buildDhallDirectoryPackage` - build a Dhall package from a local directory 235 236* `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository 237 238The `buildDhallPackage` is the lowest-level function and accepts the following 239arguments: 240 241* `name`: The name of the derivation 242 243* `dependencies`: Dhall dependencies to build and cache ahead of time 244 245* `code`: The top-level expression to build for this package 246 247 Note that the `code` field accepts an arbitrary Dhall expression. You're 248 not limited to just a file. 249 250* `source`: Set to `true` to include the decoded result as `source.dhall` in the 251 build product, at the expense of requiring more disk space 252 253* `documentationRoot`: Set to the root directory of the package if you want 254 `dhall-docs` to generate documentation underneath the `docs` subdirectory of 255 the build product 256 257The `buildDhallDirectoryPackage` is a higher-level function implemented in terms 258of `buildDhallPackage` that accepts the following arguments: 259 260* `name`: Same as `buildDhallPackage` 261 262* `dependencies`: Same as `buildDhallPackage` 263 264* `source`: Same as `buildDhallPackage` 265 266* `src`: The directory containing Dhall code that you want to turn into a Dhall 267 package 268 269* `file`: The top-level file (`package.dhall` by default) that is the entrypoint 270 to the rest of the package 271 272* `document`: Set to `true` to generate documentation for the package 273 274The `buildDhallGitHubPackage` is another higher-level function implemented in 275terms of `buildDhallPackage` that accepts the following arguments: 276 277* `name`: Same as `buildDhallPackage` 278 279* `dependencies`: Same as `buildDhallPackage` 280 281* `source`: Same as `buildDhallPackage` 282 283* `owner`: The owner of the repository 284 285* `repo`: The repository name 286 287* `rev`: The desired revision (or branch, or tag) 288 289* `directory`: The subdirectory of the Git repository to package (if a 290 directory other than the root of the repository) 291 292* `file`: The top-level file (`${directory}/package.dhall` by default) that is 293 the entrypoint to the rest of the package 294 295* `document`: Set to `true` to generate documentation for the package 296 297Additionally, `buildDhallGitHubPackage` accepts the same arguments as 298`fetchFromGitHub`, such as `hash` or `fetchSubmodules`. 299 300## `dhall-to-nixpkgs` {#ssec-dhall-dhall-to-nixpkgs} 301 302You can use the `dhall-to-nixpkgs` command-line utility to automate 303packaging Dhall code. For example: 304 305```ShellSession 306$ nix-shell -p haskellPackages.dhall-nixpkgs nix-prefetch-git 307[nix-shell]$ dhall-to-nixpkgs github https://github.com/Gabriella439/dhall-semver.git 308{ buildDhallGitHubPackage, Prelude }: 309 buildDhallGitHubPackage { 310 name = "dhall-semver"; 311 githubBase = "github.com"; 312 owner = "Gabriella439"; 313 repo = "dhall-semver"; 314 rev = "2d44ae605302ce5dc6c657a1216887fbb96392a4"; 315 fetchSubmodules = false; 316 hash = "sha256-n0nQtswVapWi/x7or0O3MEYmAkt/a1uvlOtnje6GGnk="; 317 directory = ""; 318 file = "package.dhall"; 319 source = false; 320 document = false; 321 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 322 } 323``` 324 325:::{.note} 326`nix-prefetch-git` is added to the `nix-shell -p` invocation above, because it has to be in `$PATH` for `dhall-to-nixpkgs` to work. 327::: 328 329The utility takes care of automatically detecting remote imports and converting 330them to package dependencies. You can also use the utility on local 331Dhall directories, too: 332 333```ShellSession 334$ dhall-to-nixpkgs directory ~/proj/dhall-semver 335{ buildDhallDirectoryPackage, Prelude }: 336 buildDhallDirectoryPackage { 337 name = "proj"; 338 src = ~/proj/dhall-semver; 339 file = "package.dhall"; 340 source = false; 341 document = false; 342 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 343 } 344``` 345 346### Remote imports as fixed-output derivations {#ssec-dhall-remote-imports-as-fod} 347 348`dhall-to-nixpkgs` has the ability to fetch and build remote imports as 349fixed-output derivations by using their Dhall integrity check. This is 350sometimes easier than manually packaging all remote imports. 351 352This can be used like the following: 353 354```ShellSession 355$ dhall-to-nixpkgs directory --fixed-output-derivations ~/proj/dhall-semver 356{ buildDhallDirectoryPackage, buildDhallUrl }: 357 buildDhallDirectoryPackage { 358 name = "proj"; 359 src = ~/proj/dhall-semver; 360 file = "package.dhall"; 361 source = false; 362 document = false; 363 dependencies = [ 364 (buildDhallUrl { 365 url = "https://prelude.dhall-lang.org/v17.0.0/package.dhall"; 366 hash = "sha256-ENs8kZwl6QRoM9+Jeo/+JwHcOQ+giT2VjDQwUkvlpD4="; 367 dhallHash = "sha256:10db3c919c25e9046833df897a8ffe2701dc390fa0893d958c3430524be5a43e"; 368 }) 369 ]; 370 } 371``` 372 373Here, `dhall-semver`'s `Prelude` dependency is fetched and built with the 374`buildDhallUrl` helper function, instead of being passed in as a function 375argument. 376 377## Overriding dependency versions {#ssec-dhall-overriding-dependency-versions} 378 379Suppose that we change our `true.dhall` example expression to depend on an older 380version of the Prelude (19.0.0): 381 382```dhall 383-- ./true.dhall 384 385let Prelude = 386 https://prelude.dhall-lang.org/v19.0.0/package.dhall 387 sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 388 389in Prelude.Bool.not False 390``` 391 392If we try to rebuild that expression the build will fail: 393 394```ShellSession 395$ nix build --file ./example.nix dhallPackages.true 396builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines: 397 398 Dhall was compiled without the 'with-http' flag. 399 400 The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall 401 402 403 4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall 404 5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 405 406 /nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7 407[1 built (1 failed), 0.0 MiB DL] 408error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed 409``` 410 411… because the default Prelude selected by Nixpkgs revision 412`94b2848559b12a8ed1fe433084686b2a81123c99is` is version 20.1.0, which doesn't 413have the same integrity check as version 19.0.0. This means that version 41419.0.0 is not cached and the interpreter is not allowed to fall back to 415importing the URL. 416 417However, we can override the default Prelude version by using `dhall-to-nixpkgs` 418to create a Dhall package for our desired Prelude: 419 420```ShellSession 421$ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \ 422 --name Prelude \ 423 --directory Prelude \ 424 --rev v19.0.0 \ 425 > Prelude.nix 426``` 427 428… and then referencing that package in our Dhall overlay, by either overriding 429the Prelude globally for all packages, like this: 430 431```nix 432 dhallOverrides = self: super: { 433 true = self.callPackage ./true.nix { }; 434 435 Prelude = self.callPackage ./Prelude.nix { }; 436 }; 437``` 438 439… or selectively overriding the Prelude dependency for just the `true` package, 440like this: 441 442```nix 443 dhallOverrides = self: super: { 444 true = self.callPackage ./true.nix { 445 Prelude = self.callPackage ./Prelude.nix { }; 446 }; 447 }; 448``` 449 450## Overrides {#ssec-dhall-overrides} 451 452You can override any of the arguments to `buildDhallGitHubPackage` or 453`buildDhallDirectoryPackage` using the `overridePackage` attribute of a package. 454For example, suppose we wanted to selectively enable `source = true` just for the Prelude. We can do that like this: 455 456```nix 457 dhallOverrides = self: super: { 458 Prelude = super.Prelude.overridePackage { source = true; }; 459 460461 }; 462``` 463 464[semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages