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