···
1
+
# Dhall {#sec-language-dhall}
3
+
The Nixpkgs support for Dhall assumes some familiarity with Dhall's language
4
+
support for importing Dhall expressions, which is documented here:
6
+
* [`dhall-lang.org` - Installing packages](https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages)
10
+
Nixpkgs bypasses Dhall's support for remote imports using Dhall's
11
+
semantic integrity checks. Specifically, any Dhall import can be protected by
12
+
an integrity check like:
15
+
https://prelude.dhall-lang.org/v20.1.0/package.dhall
16
+
sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
19
+
… and if the import is cached then the interpreter will load the import from
20
+
cache instead of fetching the URL.
22
+
Nixpkgs uses this trick to add all of a Dhall expression's dependencies into the
23
+
cache so that the Dhall interpreter never needs to resolve any remote URLs. In
24
+
fact, Nixpkgs uses a Dhall interpreter with remote imports disabled when
25
+
packaging Dhall expressions to enforce that the interpreter never resolves a
26
+
remote import. This means that Nixpkgs only supports building Dhall expressions
27
+
if all of their remote imports are protected by semantic integrity checks.
29
+
Instead of remote imports, Nixpkgs uses Nix to fetch remote Dhall code. For
30
+
example, the Prelude Dhall package uses `pkgs.fetchFromGitHub` to fetch the
31
+
`dhall-lang` repository containing the Prelude. Relying exclusively on Nix
32
+
to fetch Dhall code ensures that Dhall packages built using Nix remain pure and
33
+
also behave well when built within a sandbox.
35
+
## Packaging a Dhall expression from scratch
37
+
We can illustrate how Nixpkgs integrates Dhall by beginning from the following
38
+
trivial Dhall expression with one dependency (the Prelude):
43
+
let Prelude = https://prelude.dhall-lang.org/v20.1.0/package.dhall
45
+
in Prelude.Bool.not False
48
+
As written, this expression cannot be built using Nixpkgs because the
49
+
expression does not protect the Prelude import with a semantic integrity
50
+
check, so the first step is to freeze the expression using `dhall freeze`,
54
+
$ dhall freeze --inplace ./true.dhall
63
+
https://prelude.dhall-lang.org/v20.1.0/package.dhall
64
+
sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
66
+
in Prelude.Bool.not False
69
+
To package that expression, we create a `./true.nix` file containing the
70
+
following specification for the Dhall package:
75
+
{ buildDhallPackage, Prelude }:
79
+
code = ./true.dhall;
80
+
dependencies = [ Prelude ];
85
+
… and we complete the build by incorporating that Dhall package into the
86
+
`pkgs.dhallPackages` hierarchy using an overlay, like this:
92
+
nixpkgs = builtins.fetchTarball {
93
+
url = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz";
94
+
sha256 = "1pbl4c2dsaz2lximgd31m96jwbps6apn3anx8cvvhk1gl9rkg107";
97
+
dhallOverlay = self: super: {
98
+
true = self.callPackage ./true.nix { };
101
+
overlay = self: super: {
102
+
dhallPackages = super.dhallPackages.override (old: {
104
+
self.lib.composeExtensions (old.overrides or (_: _: {})) dhallOverlay;
108
+
pkgs = import nixpkgs { config = {}; overlays = [ overlay ]; };
114
+
… which we can then build using this command:
117
+
$ nix build --file ./example.nix dhallPackages.true
120
+
## Contents of a Dhall package
122
+
The above package produces the following directory tree:
129
+
│ └── 122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
136
+
* `source.dhall` contains the result of interpreting our Dhall package:
139
+
$ cat ./result/source.dhall
143
+
* The `.cache` subdirectory contains one binary cache product encoding the
144
+
same result as `source.dhall`:
147
+
$ dhall decode < ./result/.cache/dhall/122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
151
+
* `binary.dhall` contains a Dhall expression which handles fetching and decoding
152
+
the same cache product:
155
+
$ cat ./result/binary.dhall
156
+
missing sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
157
+
$ cp -r ./result/.cache .cache
159
+
$ chmod -R u+w .cache
161
+
$ XDG_CACHE_HOME=.cache dhall --file ./result/binary.dhall
165
+
The `source.dhall` file is only present for packages that specify
166
+
`source = true;`. By default, Dhall packages omit the `source.dhall` in order
167
+
to conserve disk space when they are used exclusively as dependencies. For
168
+
example, if we build the Prelude package it will only contain the binary
169
+
encoding of the expression:
172
+
$ nix build --file ./example.nix dhallPackages.Prelude
178
+
│ └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
181
+
2 directories, 2 files
184
+
Typically, you only specify `source = true;` for the top-level Dhall expression
185
+
of interest (such as our example `true.nix` Dhall package). However, if you
186
+
wish to specify `source = true` for all Dhall packages, then you can amend the
187
+
Dhall overlay like this:
190
+
dhallOverrides = self: super: {
191
+
# Enable source for all Dhall packages
192
+
buildDhallPackage =
193
+
args: super.buildDhallPackage (args // { source = true; });
195
+
true = self.callPackage ./true.nix { };
199
+
… and now the Prelude will contain the fully decoded result of interpreting
203
+
$ nix build --file ./example.nix dhallPackages.Prelude
209
+
│ └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
213
+
$ cat ./result/source.dhall
216
+
\(_ : List Bool) ->
217
+
List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 && _) True
218
+
, build = \(_ : Type -> _ -> _@1 -> _@2) -> _ Bool True False
220
+
\(_ : List Bool) ->
221
+
List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 == _) True
227
+
## Packaging functions
229
+
We already saw an example of using `buildDhallPackage` to create a Dhall
230
+
package from a single file, but most Dhall packages consist of more than one
231
+
file and there are two derived utilities that you may find more useful when
232
+
packaging multiple files:
234
+
* `buildDhallDirectoryPackage` - build a Dhall package from a local directory
236
+
* `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository
238
+
The `buildDhallPackage` is the lowest-level function and accepts the following
241
+
* `name`: The name of the derivation
243
+
* `dependencies`: Dhall dependencies to build and cache ahead of time
245
+
* `code`: The top-level expression to build for this package
247
+
Note that the `code` field accepts an arbitrary Dhall expression. You're
248
+
not limited to just a file.
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
253
+
* `documentationRoot`: Set to the root directory of the package if you want
254
+
`dhall-docs` to generate documentation underneath the `docs` subdirectory of
257
+
The `buildDhallDirectoryPackage` is a higher-level function implemented in terms
258
+
of `buildDhallPackage` that accepts the following arguments:
260
+
* `name`: Same as `buildDhallPackage`
262
+
* `dependencies`: Same as `buildDhallPackage`
264
+
* `source`: Same as `buildDhallPackage`
266
+
* `src`: The directory containing Dhall code that you want to turn into a Dhall
269
+
* `file`: The top-level file (`package.dhall` by default) that is the entrypoint
270
+
to the rest of the package
272
+
* `document`: Set to `true` to generate documentation for the package
274
+
The `buildDhallGitHubPackage` is another higher-level function implemented in
275
+
terms of `buildDhallPackage` that accepts the following arguments:
277
+
* `name`: Same as `buildDhallPackage`
279
+
* `dependencies`: Same as `buildDhallPackage`
281
+
* `source`: Same as `buildDhallPackage`
283
+
* `owner`: The owner of the repository
285
+
* `repo`: The repository name
287
+
* `rev`: The desired revision (or branch, or tag)
289
+
* `directory`: The subdirectory of the Git repository to package (if a
290
+
directory other than the root of the repository)
292
+
* `file`: The top-level file (`${directory}/package.dhall` by default) that is
293
+
the entrypoint to the rest of the package
295
+
* `document`: Set to `true` to generate documentation for the package
297
+
Additionally, `buildDhallGitHubPackage` accepts the same arguments as
298
+
`fetchFromGitHub`, such as `sha256` or `fetchSubmodules`.
300
+
## `dhall-to-nixpkgs`
302
+
You can use the `dhall-to-nixpkgs` command-line utility to automate
303
+
packaging Dhall code. For example:
306
+
$ nix-env --install --attr haskellPackages.dhall-nixpkgs
308
+
$ nix-env --install --attr nix-prefetch-git # Used by dhall-to-nixpkgs
310
+
$ dhall-to-nixpkgs github https://github.com/Gabriel439/dhall-semver.git
311
+
{ buildDhallGitHubPackage, Prelude }:
312
+
buildDhallGitHubPackage {
313
+
name = "dhall-semver";
314
+
githubBase = "github.com";
315
+
owner = "Gabriel439";
316
+
repo = "dhall-semver";
317
+
rev = "2d44ae605302ce5dc6c657a1216887fbb96392a4";
318
+
fetchSubmodules = false;
319
+
sha256 = "0y8shvp8srzbjjpmnsvz9c12ciihnx1szs0yzyi9ashmrjvd0jcz";
321
+
file = "package.dhall";
324
+
dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ];
328
+
The utility takes care of automatically detecting remote imports and converting
329
+
them to package dependencies. You can also use the utility on local
330
+
Dhall directories, too:
333
+
$ dhall-to-nixpkgs directory ~/proj/dhall-semver
334
+
{ buildDhallDirectoryPackage, Prelude }:
335
+
buildDhallDirectoryPackage {
337
+
src = /Users/gabriel/proj/dhall-semver;
338
+
file = "package.dhall";
341
+
dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ];
345
+
## Overriding dependency versions
347
+
Suppose that we change our `true.dhall` example expression to depend on an older
348
+
version of the Prelude (19.0.0):
354
+
https://prelude.dhall-lang.org/v19.0.0/package.dhall
355
+
sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2
357
+
in Prelude.Bool.not False
360
+
If we try to rebuild that expression the build will fail:
363
+
$ nix build --file ./example.nix dhallPackages.true
364
+
builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines:
366
+
Dhall was compiled without the 'with-http' flag.
368
+
The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall
371
+
4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall
372
+
5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2
374
+
/nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7
375
+
[1 built (1 failed), 0.0 MiB DL]
376
+
error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed
379
+
… because the default Prelude selected by Nixpkgs revision
380
+
`94b2848559b12a8ed1fe433084686b2a81123c99is` is version 20.1.0, which doesn't
381
+
have the same integrity check as version 19.0.0. This means that version
382
+
19.0.0 is not cached and the interpreter is not allowed to fall back to
385
+
However, we can override the default Prelude version by using `dhall-to-nixpkgs`
386
+
to create a Dhall package for our desired Prelude:
389
+
$ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \
391
+
--directory Prelude \
396
+
… and then referencing that package in our Dhall overlay, by either overriding
397
+
the Prelude globally for all packages, like this:
400
+
dhallOverrides = self: super: {
401
+
true = self.callPackage ./true.nix { };
403
+
Prelude = self.callPackage ./Prelude.nix { };
407
+
… or selectively overriding the Prelude dependency for just the `true` package,
411
+
dhallOverrides = self: super: {
412
+
true = self.callPackage ./true.nix {
413
+
Prelude = self.callPackage ./Prelude.nix { };
420
+
You can override any of the arguments to `buildDhallGitHubPackage` or
421
+
`buildDhallDirectoryPackage` using the `overridePackage` attribute of a package.
422
+
For example, suppose we wanted to selectively enable `source = true` just for the Prelude. We can do that like this:
425
+
dhallOverrides = self: super: {
426
+
Prelude = super.Prelude.overridePackage { source = true; };
432
+
[semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages