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