envision-unwrapped: 0-unstable-2024-10-20 -> 1.1.1 (#360652)
[NixPkgs.git] / doc / languages-frameworks / dhall.section.md
blob8d85c9f1daf75b05620ffe903c0dfd5291ed0e37
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)
8 ## Remote imports {#ssec-dhall-remote-imports}
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:
14 ```dhall
15 https://prelude.dhall-lang.org/v20.1.0/package.dhall
16   sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
17 ```
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 {#ssec-dhall-packaging-expression}
37 We can illustrate how Nixpkgs integrates Dhall by beginning from the following
38 trivial Dhall expression with one dependency (the Prelude):
40 ```dhall
41 -- ./true.dhall
43 let Prelude = https://prelude.dhall-lang.org/v20.1.0/package.dhall
45 in  Prelude.Bool.not False
46 ```
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`,
51 like this:
53 ```ShellSession
54 $ dhall freeze --inplace ./true.dhall
55 ```
57 … which gives us:
59 ```dhall
60 -- ./true.dhall
62 let Prelude =
63       https://prelude.dhall-lang.org/v20.1.0/package.dhall
64         sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
66 in  Prelude.Bool.not False
67 ```
69 To package that expression, we create a `./true.nix` file containing the
70 following specification for the Dhall package:
72 ```nix
73 # ./true.nix
75 { buildDhallPackage, Prelude }:
77 buildDhallPackage {
78   name = "true";
79   code = ./true.dhall;
80   dependencies = [ Prelude ];
81   source = true;
83 ```
85 … and we complete the build by incorporating that Dhall package into the
86 `pkgs.dhallPackages` hierarchy using an overlay, like this:
88 ```nix
89 # ./example.nix
91 let
92   nixpkgs = builtins.fetchTarball {
93     url    = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz";
94     hash = "sha256-B4Q3c6IvTLg3Q92qYa8y+i4uTaphtFdjp+Ir3QQjdN0=";
95   };
97   dhallOverlay = self: super: {
98     true = self.callPackage ./true.nix { };
99   };
101   overlay = self: super: {
102     dhallPackages = super.dhallPackages.override (old: {
103       overrides =
104         self.lib.composeExtensions (old.overrides or (_: _: {})) dhallOverlay;
105     });
106   };
108   pkgs = import nixpkgs { config = {}; overlays = [ overlay ]; };
111   pkgs
114 … which we can then build using this command:
116 ```ShellSession
117 $ nix build --file ./example.nix dhallPackages.true
120 ## Contents of a Dhall package {#ssec-dhall-package-contents}
122 The above package produces the following directory tree:
124 ```ShellSession
125 $ tree -a ./result
126 result
127 ├── .cache
128 │   └── dhall
129 │       └── 122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
130 ├── binary.dhall
131 └── source.dhall
134 … where:
136 * `source.dhall` contains the result of interpreting our Dhall package:
138   ```ShellSession
139   $ cat ./result/source.dhall
140   True
141   ```
143 * The `.cache` subdirectory contains one binary cache product encoding the
144   same result as `source.dhall`:
146   ```ShellSession
147   $ dhall decode < ./result/.cache/dhall/122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70
148   True
149   ```
151 * `binary.dhall` contains a Dhall expression which handles fetching and decoding
152   the same cache product:
154   ```ShellSession
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
162   True
163   ```
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:
171 ```ShellSession
172 $ nix build --file ./example.nix dhallPackages.Prelude
174 $ tree -a result
175 result
176 ├── .cache
177 │   └── dhall
178 │       └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
179 └── binary.dhall
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:
189 ```nix
191   dhallOverrides = self: super: {
192     # Enable source for all Dhall packages
193     buildDhallPackage =
194       args: super.buildDhallPackage (args // { source = true; });
196     true = self.callPackage ./true.nix { };
197   };
201 … and now the Prelude will contain the fully decoded result of interpreting
202 the Prelude:
204 ```ShellSession
205 $ nix build --file ./example.nix dhallPackages.Prelude
207 $ tree -a result
208 result
209 ├── .cache
210 │   └── dhall
211 │       └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
212 ├── binary.dhall
213 └── source.dhall
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) ->
229 ## Packaging functions {#ssec-dhall-packaging-functions}
231 We already saw an example of using `buildDhallPackage` to create a Dhall
232 package from a single file, but most Dhall packages consist of more than one
233 file and there are two derived utilities that you may find more useful when
234 packaging multiple files:
236 * `buildDhallDirectoryPackage` - build a Dhall package from a local directory
238 * `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository
240 The `buildDhallPackage` is the lowest-level function and accepts the following
241 arguments:
243 * `name`: The name of the derivation
245 * `dependencies`: Dhall dependencies to build and cache ahead of time
247 * `code`: The top-level expression to build for this package
249   Note that the `code` field accepts an arbitrary Dhall expression.  You're
250   not limited to just a file.
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
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
259 The `buildDhallDirectoryPackage` is a higher-level function implemented in terms
260 of `buildDhallPackage` that accepts the following arguments:
262 * `name`: Same as `buildDhallPackage`
264 * `dependencies`: Same as `buildDhallPackage`
266 * `source`: Same as `buildDhallPackage`
268 * `src`: The directory containing Dhall code that you want to turn into a Dhall
269   package
271 * `file`: The top-level file (`package.dhall` by default) that is the entrypoint
272   to the rest of the package
274 * `document`: Set to `true` to generate documentation for the package
276 The `buildDhallGitHubPackage` is another higher-level function implemented in
277 terms of `buildDhallPackage` that accepts the following arguments:
279 * `name`: Same as `buildDhallPackage`
281 * `dependencies`: Same as `buildDhallPackage`
283 * `source`: Same as `buildDhallPackage`
285 * `owner`: The owner of the repository
287 * `repo`: The repository name
289 * `rev`: The desired revision (or branch, or tag)
291 * `directory`: The subdirectory of the Git repository to package (if a
292   directory other than the root of the repository)
294 * `file`: The top-level file (`${directory}/package.dhall` by default) that is
295   the entrypoint to the rest of the package
297 * `document`: Set to `true` to generate documentation for the package
299 Additionally, `buildDhallGitHubPackage` accepts the same arguments as
300 `fetchFromGitHub`, such as `hash` or `fetchSubmodules`.
302 ## `dhall-to-nixpkgs` {#ssec-dhall-dhall-to-nixpkgs}
304 You can use the `dhall-to-nixpkgs` command-line utility to automate
305 packaging Dhall code.  For example:
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     }
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.
331 The utility takes care of automatically detecting remote imports and converting
332 them to package dependencies.  You can also use the utility on local
333 Dhall directories, too:
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     }
348 ### Remote imports as fixed-output derivations {#ssec-dhall-remote-imports-as-fod}
350 `dhall-to-nixpkgs` has the ability to fetch and build remote imports as
351 fixed-output derivations by using their Dhall integrity check. This is
352 sometimes easier than manually packaging all remote imports.
354 This can be used like the following:
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     }
375 Here, `dhall-semver`'s `Prelude` dependency is fetched and built with the
376 `buildDhallUrl` helper function, instead of being passed in as a function
377 argument.
379 ## Overriding dependency versions {#ssec-dhall-overriding-dependency-versions}
381 Suppose that we change our `true.dhall` example expression to depend on an older
382 version of the Prelude (19.0.0):
384 ```dhall
385 -- ./true.dhall
387 let Prelude =
388       https://prelude.dhall-lang.org/v19.0.0/package.dhall
389         sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2
391 in  Prelude.Bool.not False
394 If we try to rebuild that expression the build will fail:
396 ```ShellSession
397 $ nix build --file ./example.nix dhallPackages.true
398 builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines:
400   Dhall was compiled without the 'with-http' flag.
402   The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall
405   4│       https://prelude.dhall-lang.org/v19.0.0/package.dhall
406   5│         sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2
408   /nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7
409 [1 built (1 failed), 0.0 MiB DL]
410 error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed
413 … because the default Prelude selected by Nixpkgs revision
414 `94b2848559b12a8ed1fe433084686b2a81123c99is` is version 20.1.0, which doesn't
415 have the same integrity check as version 19.0.0.  This means that version
416 19.0.0 is not cached and the interpreter is not allowed to fall back to
417 importing the URL.
419 However, we can override the default Prelude version by using `dhall-to-nixpkgs`
420 to create a Dhall package for our desired Prelude:
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
430 … and then referencing that package in our Dhall overlay, by either overriding
431 the Prelude globally for all packages, like this:
433 ```nix
435   dhallOverrides = self: super: {
436     true = self.callPackage ./true.nix { };
438     Prelude = self.callPackage ./Prelude.nix { };
439   };
443 … or selectively overriding the Prelude dependency for just the `true` package,
444 like this:
446 ```nix
448   dhallOverrides = self: super: {
449     true = self.callPackage ./true.nix {
450       Prelude = self.callPackage ./Prelude.nix { };
451     };
452   };
456 ## Overrides {#ssec-dhall-overrides}
458 You can override any of the arguments to `buildDhallGitHubPackage` or
459 `buildDhallDirectoryPackage` using the `overridePackage` attribute of a package.
460 For example, suppose we wanted to selectively enable `source = true` just for the Prelude.  We can do that like this:
462 ```nix
464   dhallOverrides = self: super: {
465     Prelude = super.Prelude.overridePackage { source = true; };
467     # ...
468   };
472 [semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages