dhall: Document language support in the Nixpkgs manual (#123051)

authored by Gabriel Gonzalez and committed by GitHub b23797e2 b065b36e

+433
+432
doc/languages-frameworks/dhall.section.md
···
··· 1 + # Dhall {#sec-language-dhall} 2 + 3 + The Nixpkgs support for Dhall assumes some familiarity with Dhall's language 4 + support 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 9 + 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: 13 + 14 + ```dhall 15 + https://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 20 + cache instead of fetching the URL. 21 + 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. 28 + 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. 34 + 35 + ## Packaging a Dhall expression from scratch 36 + 37 + We can illustrate how Nixpkgs integrates Dhall by beginning from the following 38 + trivial Dhall expression with one dependency (the Prelude): 39 + 40 + ```dhall 41 + -- ./true.dhall 42 + 43 + let Prelude = https://prelude.dhall-lang.org/v20.1.0/package.dhall 44 + 45 + in Prelude.Bool.not False 46 + ``` 47 + 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: 52 + 53 + ```bash 54 + $ dhall freeze --inplace ./true.dhall 55 + ``` 56 + 57 + … which gives us: 58 + 59 + ```dhall 60 + -- ./true.dhall 61 + 62 + let Prelude = 63 + https://prelude.dhall-lang.org/v20.1.0/package.dhall 64 + sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 65 + 66 + in Prelude.Bool.not False 67 + ``` 68 + 69 + To package that expression, we create a `./true.nix` file containing the 70 + following specification for the Dhall package: 71 + 72 + ```nix 73 + # ./true.nix 74 + 75 + { buildDhallPackage, Prelude }: 76 + 77 + buildDhallPackage { 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 + 91 + let 92 + nixpkgs = builtins.fetchTarball { 93 + url = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz"; 94 + sha256 = "1pbl4c2dsaz2lximgd31m96jwbps6apn3anx8cvvhk1gl9rkg107"; 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 + 110 + in 111 + pkgs 112 + ``` 113 + 114 + … which we can then build using this command: 115 + 116 + ```bash 117 + $ nix build --file ./example.nix dhallPackages.true 118 + ``` 119 + 120 + ## Contents of a Dhall package 121 + 122 + The above package produces the following directory tree: 123 + 124 + ```bash 125 + $ tree -a ./result 126 + result 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 + ```bash 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 + ```bash 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 + ```bash 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 + 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: 170 + 171 + ```bash 172 + $ nix build --file ./example.nix dhallPackages.Prelude 173 + 174 + $ tree -a result 175 + result 176 + ├── .cache 177 + │   └── dhall 178 + │   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 179 + └── binary.dhall 180 + 181 + 2 directories, 2 files 182 + ``` 183 + 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: 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 200 + the Prelude: 201 + 202 + ```bash 203 + $ nix build --file ./example.nix dhallPackages.Prelude 204 + 205 + $ tree -a result 206 + result 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 228 + 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: 233 + 234 + * `buildDhallDirectoryPackage` - build a Dhall package from a local directory 235 + 236 + * `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository 237 + 238 + The `buildDhallPackage` is the lowest-level function and accepts the following 239 + arguments: 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 + 257 + The `buildDhallDirectoryPackage` is a higher-level function implemented in terms 258 + of `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 + 274 + The `buildDhallGitHubPackage` is another higher-level function implemented in 275 + terms 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 + 297 + Additionally, `buildDhallGitHubPackage` accepts the same arguments as 298 + `fetchFromGitHub`, such as `sha256` or `fetchSubmodules`. 299 + 300 + ## `dhall-to-nixpkgs` 301 + 302 + You can use the `dhall-to-nixpkgs` command-line utility to automate 303 + packaging Dhall code. For example: 304 + 305 + ```bash 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/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"; 320 + directory = ""; 321 + file = "package.dhall"; 322 + source = false; 323 + document = false; 324 + dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 325 + } 326 + ``` 327 + 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: 331 + 332 + ```bash 333 + $ dhall-to-nixpkgs directory ~/proj/dhall-semver 334 + { buildDhallDirectoryPackage, Prelude }: 335 + buildDhallDirectoryPackage { 336 + name = "proj"; 337 + src = /Users/gabriel/proj/dhall-semver; 338 + file = "package.dhall"; 339 + source = false; 340 + document = false; 341 + dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 342 + } 343 + ``` 344 + 345 + ## Overriding dependency versions 346 + 347 + Suppose that we change our `true.dhall` example expression to depend on an older 348 + version of the Prelude (19.0.0): 349 + 350 + ```dhall 351 + -- ./true.dhall 352 + 353 + let Prelude = 354 + https://prelude.dhall-lang.org/v19.0.0/package.dhall 355 + sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 356 + 357 + in Prelude.Bool.not False 358 + ``` 359 + 360 + If we try to rebuild that expression the build will fail: 361 + 362 + ``` 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: 365 + 366 + Dhall was compiled without the 'with-http' flag. 367 + 368 + The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall 369 + 370 + 371 + 4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall 372 + 5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 373 + 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 377 + ``` 378 + 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 383 + importing the URL. 384 + 385 + However, we can override the default Prelude version by using `dhall-to-nixpkgs` 386 + to create a Dhall package for our desired Prelude: 387 + 388 + ```bash 389 + $ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \ 390 + --name Prelude \ 391 + --directory Prelude \ 392 + --rev v19.0.0 \ 393 + > Prelude.nix 394 + ``` 395 + 396 + … and then referencing that package in our Dhall overlay, by either overriding 397 + the Prelude globally for all packages, like this: 398 + 399 + ```bash 400 + dhallOverrides = self: super: { 401 + true = self.callPackage ./true.nix { }; 402 + 403 + Prelude = self.callPackage ./Prelude.nix { }; 404 + }; 405 + ``` 406 + 407 + … or selectively overriding the Prelude dependency for just the `true` package, 408 + like this: 409 + 410 + ```bash 411 + dhallOverrides = self: super: { 412 + true = self.callPackage ./true.nix { 413 + Prelude = self.callPackage ./Prelude.nix { }; 414 + }; 415 + }; 416 + ``` 417 + 418 + ## Overrides 419 + 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: 423 + 424 + ```nix 425 + dhallOverrides = self: super: { 426 + Prelude = super.Prelude.overridePackage { source = true; }; 427 + 428 + 429 + }; 430 + ``` 431 + 432 + [semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages
+1
doc/languages-frameworks/index.xml
··· 11 <xi:include href="bower.section.xml" /> 12 <xi:include href="coq.section.xml" /> 13 <xi:include href="crystal.section.xml" /> 14 <xi:include href="emscripten.section.xml" /> 15 <xi:include href="gnome.section.xml" /> 16 <xi:include href="go.section.xml" />
··· 11 <xi:include href="bower.section.xml" /> 12 <xi:include href="coq.section.xml" /> 13 <xi:include href="crystal.section.xml" /> 14 + <xi:include href="dhall.section.xml" /> 15 <xi:include href="emscripten.section.xml" /> 16 <xi:include href="gnome.section.xml" /> 17 <xi:include href="go.section.xml" />