Advice for using Dhall within NixOS modules


#1

I’m in the process of creating a NixOS derivative that uses an init system other than systemd. This init is special in that it is configured with XML, and I’ve already written a Dhall library that covers most of the axes of configuration. Eventually the init could be forked and made Dhall native, but not sometime soon. This is also a non-unix init, but that not important here, and that’s what I’m ultimately trying to abstract away.

I’ve started wrapping the Dhall using NixOS modules, and frankly its a bit awkward. A good example of this would be the translation of networking.interfaces: https://git.sr.ht/~ehmry/genodepkgs/tree/staging/nixos-modules/hardware.nix For this platform all the drivers are in userspace and need to be injected into the init system where they are needed.

The implementations of the modules are of course the hardest to read. A user-supplied configuration with some inline dhall looks like this: https://git.sr.ht/~ehmry/genodepkgs/tree/staging/tests/networking.nix

I would rather not bypass Dhall and generate XML from Nix because the Dhall wrappers infer some of configuration during rendering and I prefer to keep that logic in Dhall.

From what I’ve experienced so far it seems that Dhall code is best represented by the Nix type types.path, which is a file in /nix/store, produced by either pkgs.writeText or builtins.toFile. This forces all user-supplied Dhall configuration to type-check and be without free-variables before being mixed with other configuration. Within the configurations I use the same env:DHALL_GENODE import everywhere, which I can easily set when I actually render the final configuration. There is a lot of raw Dhall code with a few text-interpolations, but in other places I’ve been writing Dhall functions in seperate files and calling them from Nix like pkgs.writeText "foobar.dhall" ''${./foo.dhall} { bar = ${bar} }''. I will probably write a configuration generator for Dhall as promoted by NixOS RFC42, but I figure I will gather a bit more experience first.

Is anyone else doing anything similar? Am I missing a better way to do this? I’m juggling a number of other tasks at for this project, so I worry I’m overlooking something here.


#2

@ehmry: The only approach safer than string interpolation that I’m aware of is to use something like pkgs.dhallToNix. In other words:

pkgs.dhallToNix "${./foo.dhall}" { inherit bar; }

#3

I haven’t tried pkgs.dhallToNix yet because each item managed by the init system is a psuedo-recursive Init/Child/Type, and I don’t think I can convert that to Nix. If I could convert an expression within Dhall to a Text value and hand that off to Nix then this would be cleaner, but I don’t want to make a feature request yet.

It would be interesting to generate the bulk of a NixOS module using dhallToNix, but again I think there are too many mismatched language features.


#4

@ehmry: pkgs.dhallToNix supports essentially all of Dhall, with the exception of record projection by type. For example, Prelude.JSON.Type is also a recursive representation, and it is convertable to Nix:

dhall-to-nix <<< 'let Prelude = https://prelude.dhall-lang.org/package.dhall in Prelude.JSON.null'
_1:
  _:
    _.null

However, you don’t necessarily need to use that. You can alternatively perform the conversion of the recursive expression to Text within Dhall, in which case pkgs.dhallToNix would return a Nix string.