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 20the cache 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 = fetchTarball { 93 url = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz"; 94 hash = "sha256-B4Q3c6IvTLg3Q92qYa8y+i4uTaphtFdjp+Ir3QQjdN0="; 95 }; 96 97 dhallOverlay = self: super: { true = self.callPackage ./true.nix { }; }; 98 99 overlay = self: super: { 100 dhallPackages = super.dhallPackages.override (old: { 101 overrides = self.lib.composeExtensions (old.overrides or (_: _: { })) dhallOverlay; 102 }); 103 }; 104 105 pkgs = import nixpkgs { 106 config = { }; 107 overlays = [ overlay ]; 108 }; 109 110in 111pkgs 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{ 191 dhallOverrides = self: super: { 192 # Enable source for all Dhall packages 193 buildDhallPackage = args: super.buildDhallPackage (args // { source = true; }); 194 195 true = self.callPackage ./true.nix { }; 196 }; 197} 198``` 199 200… and now the Prelude will contain the fully decoded result of interpreting 201the Prelude: 202 203```ShellSession 204$ nix build --file ./example.nix dhallPackages.Prelude 205 206$ tree -a result 207result 208├── .cache 209│   └── dhall 210│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 211├── binary.dhall 212└── source.dhall 213 214$ cat ./result/source.dhall 215{ Bool = 216 { and = 217 \(_ : List Bool) -> 218 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 && _) True 219 , build = \(_ : Type -> _ -> _@1 -> _@2) -> _ Bool True False 220 , even = 221 \(_ : List Bool) -> 222 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 == _) True 223 , fold = 224 \(_ : Bool) -> 225226``` 227 228## Packaging functions {#ssec-dhall-packaging-functions} 229 230We already saw an example of using `buildDhallPackage` to create a Dhall 231package from a single file, but most Dhall packages consist of more than one 232file and there are two derived utilities that you may find more useful when 233packaging multiple files: 234 235* `buildDhallDirectoryPackage` - build a Dhall package from a local directory 236 237* `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository 238 239The `buildDhallPackage` is the lowest-level function and accepts the following 240arguments: 241 242* `name`: The name of the derivation 243 244* `dependencies`: Dhall dependencies to build and cache ahead of time 245 246* `code`: The top-level expression to build for this package 247 248 Note that the `code` field accepts an arbitrary Dhall expression. You're 249 not limited to just a file. 250 251* `source`: Set to `true` to include the decoded result as `source.dhall` in the 252 build product, at the expense of requiring more disk space 253 254* `documentationRoot`: Set to the root directory of the package if you want 255 `dhall-docs` to generate documentation underneath the `docs` subdirectory of 256 the build product 257 258The `buildDhallDirectoryPackage` is a higher-level function implemented in terms 259of `buildDhallPackage` that accepts the following arguments: 260 261* `name`: Same as `buildDhallPackage` 262 263* `dependencies`: Same as `buildDhallPackage` 264 265* `source`: Same as `buildDhallPackage` 266 267* `src`: The directory containing Dhall code that you want to turn into a Dhall 268 package 269 270* `file`: The top-level file (`package.dhall` by default) that is the entry point 271 to the rest of the package 272 273* `document`: Set to `true` to generate documentation for the package 274 275The `buildDhallGitHubPackage` is another higher-level function implemented in 276terms of `buildDhallPackage` that accepts the following arguments: 277 278* `name`: Same as `buildDhallPackage` 279 280* `dependencies`: Same as `buildDhallPackage` 281 282* `source`: Same as `buildDhallPackage` 283 284* `owner`: The owner of the repository 285 286* `repo`: The repository name 287 288* `rev`: The desired revision (or branch, or tag) 289 290* `directory`: The subdirectory of the Git repository to package (if a 291 directory other than the root of the repository) 292 293* `file`: The top-level file (`${directory}/package.dhall` by default) that is 294 the entry point to the rest of the package 295 296* `document`: Set to `true` to generate documentation for the package 297 298Additionally, `buildDhallGitHubPackage` accepts the same arguments as 299`fetchFromGitHub`, such as `hash` or `fetchSubmodules`. 300 301## `dhall-to-nixpkgs` {#ssec-dhall-dhall-to-nixpkgs} 302 303You can use the `dhall-to-nixpkgs` command-line utility to automate 304packaging Dhall code. For example: 305 306```ShellSession 307$ nix-shell -p haskellPackages.dhall-nixpkgs nix-prefetch-git 308[nix-shell]$ dhall-to-nixpkgs github https://github.com/Gabriella439/dhall-semver.git 309{ buildDhallGitHubPackage, Prelude }: 310 buildDhallGitHubPackage { 311 name = "dhall-semver"; 312 githubBase = "github.com"; 313 owner = "Gabriella439"; 314 repo = "dhall-semver"; 315 rev = "2d44ae605302ce5dc6c657a1216887fbb96392a4"; 316 fetchSubmodules = false; 317 hash = "sha256-n0nQtswVapWi/x7or0O3MEYmAkt/a1uvlOtnje6GGnk="; 318 directory = ""; 319 file = "package.dhall"; 320 source = false; 321 document = false; 322 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 323 } 324``` 325 326:::{.note} 327`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. 328::: 329 330The utility takes care of automatically detecting remote imports and converting 331them to package dependencies. You can also use the utility on local 332Dhall directories, too: 333 334```ShellSession 335$ dhall-to-nixpkgs directory ~/proj/dhall-semver 336{ buildDhallDirectoryPackage, Prelude }: 337 buildDhallDirectoryPackage { 338 name = "proj"; 339 src = ~/proj/dhall-semver; 340 file = "package.dhall"; 341 source = false; 342 document = false; 343 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 344 } 345``` 346 347### Remote imports as fixed-output derivations {#ssec-dhall-remote-imports-as-fod} 348 349`dhall-to-nixpkgs` has the ability to fetch and build remote imports as 350fixed-output derivations by using their Dhall integrity check. This is 351sometimes easier than manually packaging all remote imports. 352 353This can be used like the following: 354 355```ShellSession 356$ dhall-to-nixpkgs directory --fixed-output-derivations ~/proj/dhall-semver 357{ buildDhallDirectoryPackage, buildDhallUrl }: 358 buildDhallDirectoryPackage { 359 name = "proj"; 360 src = ~/proj/dhall-semver; 361 file = "package.dhall"; 362 source = false; 363 document = false; 364 dependencies = [ 365 (buildDhallUrl { 366 url = "https://prelude.dhall-lang.org/v17.0.0/package.dhall"; 367 hash = "sha256-ENs8kZwl6QRoM9+Jeo/+JwHcOQ+giT2VjDQwUkvlpD4="; 368 dhallHash = "sha256:10db3c919c25e9046833df897a8ffe2701dc390fa0893d958c3430524be5a43e"; 369 }) 370 ]; 371 } 372``` 373 374Here, `dhall-semver`'s `Prelude` dependency is fetched and built with the 375`buildDhallUrl` helper function, instead of being passed in as a function 376argument. 377 378## Overriding dependency versions {#ssec-dhall-overriding-dependency-versions} 379 380Suppose that we change our `true.dhall` example expression to depend on an older 381version of the Prelude (19.0.0): 382 383```dhall 384-- ./true.dhall 385 386let Prelude = 387 https://prelude.dhall-lang.org/v19.0.0/package.dhall 388 sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 389 390in Prelude.Bool.not False 391``` 392 393If we try to rebuild that expression the build will fail: 394 395```ShellSession 396$ nix build --file ./example.nix dhallPackages.true 397builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines: 398 399 Dhall was compiled without the 'with-http' flag. 400 401 The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall 402 403 404 4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall 405 5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 406 407 /nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7 408[1 built (1 failed), 0.0 MiB DL] 409error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed 410``` 411 412… because the default Prelude selected by Nixpkgs revision 413`94b2848559b12a8ed1fe433084686b2a81123c99` is version 20.1.0, which doesn't 414have the same integrity check as version 19.0.0. This means that version 41519.0.0 is not cached, and the interpreter is not allowed to fall back to 416importing the URL. 417 418However, we can override the default Prelude version by using `dhall-to-nixpkgs` 419to create a Dhall package for our desired Prelude: 420 421```ShellSession 422$ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \ 423 --name Prelude \ 424 --directory Prelude \ 425 --rev v19.0.0 \ 426 > Prelude.nix 427``` 428 429… and then referencing that package in our Dhall overlay, by either overriding 430the Prelude globally for all packages, like this: 431 432```nix 433{ 434 dhallOverrides = self: super: { 435 true = self.callPackage ./true.nix { }; 436 437 Prelude = self.callPackage ./Prelude.nix { }; 438 }; 439} 440``` 441 442… or selectively overriding the Prelude dependency for just the `true` package, 443like this: 444 445```nix 446{ 447 dhallOverrides = self: super: { 448 true = self.callPackage ./true.nix { 449 Prelude = self.callPackage ./Prelude.nix { }; 450 }; 451 }; 452} 453``` 454 455## Overrides {#ssec-dhall-overrides} 456 457You can override any of the arguments to `buildDhallGitHubPackage` or 458`buildDhallDirectoryPackage` using the `overridePackage` attribute of a package. 459For example, suppose we wanted to selectively enable `source = true` just for the Prelude. We can do that like this: 460 461```nix 462{ 463 dhallOverrides = self: super: { 464 Prelude = super.Prelude.overridePackage { source = true; }; 465 466 # ... 467 }; 468} 469``` 470 471[semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages