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 sha256 = "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) ->
224…
225```
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-env --install --attr haskellPackages.dhall-nixpkgs
307
308$ nix-env --install --attr nix-prefetch-git # Used by dhall-to-nixpkgs
309
310$ 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
328The utility takes care of automatically detecting remote imports and converting
329them to package dependencies. You can also use the utility on local
330Dhall directories, too:
331
332```ShellSession
333$ dhall-to-nixpkgs directory ~/proj/dhall-semver
334{ buildDhallDirectoryPackage, Prelude }:
335 buildDhallDirectoryPackage {
336 name = "proj";
337 src = ~/proj/dhall-semver;
338 file = "package.dhall";
339 source = false;
340 document = false;
341 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ];
342 }
343```
344
345### Remote imports as fixed-output derivations {#ssec-dhall-remote-imports-as-fod}
346
347`dhall-to-nixpkgs` has the ability to fetch and build remote imports as
348fixed-output derivations by using their Dhall integrity check. This is
349sometimes easier than manually packaging all remote imports.
350
351This can be used like the following:
352
353```ShellSession
354$ dhall-to-nixpkgs directory --fixed-output-derivations ~/proj/dhall-semver
355{ buildDhallDirectoryPackage, buildDhallUrl }:
356 buildDhallDirectoryPackage {
357 name = "proj";
358 src = ~/proj/dhall-semver;
359 file = "package.dhall";
360 source = false;
361 document = false;
362 dependencies = [
363 (buildDhallUrl {
364 url = "https://prelude.dhall-lang.org/v17.0.0/package.dhall";
365 hash = "sha256-ENs8kZwl6QRoM9+Jeo/+JwHcOQ+giT2VjDQwUkvlpD4=";
366 dhallHash = "sha256:10db3c919c25e9046833df897a8ffe2701dc390fa0893d958c3430524be5a43e";
367 })
368 ];
369 }
370```
371
372Here, `dhall-semver`'s `Prelude` dependency is fetched and built with the
373`buildDhallUrl` helper function, instead of being passed in as a function
374argument.
375
376## Overriding dependency versions {#ssec-dhall-overriding-dependency-versions}
377
378Suppose that we change our `true.dhall` example expression to depend on an older
379version of the Prelude (19.0.0):
380
381```dhall
382-- ./true.dhall
383
384let Prelude =
385 https://prelude.dhall-lang.org/v19.0.0/package.dhall
386 sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2
387
388in Prelude.Bool.not False
389```
390
391If we try to rebuild that expression the build will fail:
392
393```ShellSession
394$ nix build --file ./example.nix dhallPackages.true
395builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines:
396
397 Dhall was compiled without the 'with-http' flag.
398
399 The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall
400
401
402 4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall
403 5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2
404
405 /nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7
406[1 built (1 failed), 0.0 MiB DL]
407error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed
408```
409
410… because the default Prelude selected by Nixpkgs revision
411`94b2848559b12a8ed1fe433084686b2a81123c99is` is version 20.1.0, which doesn't
412have the same integrity check as version 19.0.0. This means that version
41319.0.0 is not cached and the interpreter is not allowed to fall back to
414importing the URL.
415
416However, we can override the default Prelude version by using `dhall-to-nixpkgs`
417to create a Dhall package for our desired Prelude:
418
419```ShellSession
420$ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \
421 --name Prelude \
422 --directory Prelude \
423 --rev v19.0.0 \
424 > Prelude.nix
425```
426
427… and then referencing that package in our Dhall overlay, by either overriding
428the Prelude globally for all packages, like this:
429
430```nix
431 dhallOverrides = self: super: {
432 true = self.callPackage ./true.nix { };
433
434 Prelude = self.callPackage ./Prelude.nix { };
435 };
436```
437
438… or selectively overriding the Prelude dependency for just the `true` package,
439like this:
440
441```nix
442 dhallOverrides = self: super: {
443 true = self.callPackage ./true.nix {
444 Prelude = self.callPackage ./Prelude.nix { };
445 };
446 };
447```
448
449## Overrides {#ssec-dhall-overrides}
450
451You can override any of the arguments to `buildDhallGitHubPackage` or
452`buildDhallDirectoryPackage` using the `overridePackage` attribute of a package.
453For example, suppose we wanted to selectively enable `source = true` just for the Prelude. We can do that like this:
454
455```nix
456 dhallOverrides = self: super: {
457 Prelude = super.Prelude.overridePackage { source = true; };
458
459 …
460 };
461```
462
463[semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages