Making the Nix Architecture More Flexible

Disclaimer: I’ve only been using NixOS etc for about 6 months and I haven’t done built any packages or written custom modules, so I’m still kind of a noob.

While learning Nix, I have come up against three issues which I believe could hypothetically be solved by the same solution.

Issue 1:
Nix has a limited audience because it forces everyone to learn the logic behind reproducing their system with the functional programming. I believe I have gotten over the biggest hump of the learning curve (finally!), but it makes it hard to pitch to people that just want reproducible apps and configs without learning how variable inheritance and string concatenation works in the Nix language.

Issue 2:
Nix aims to allow people to make their configs and packages stateless, which is great, but everyone wants some state. Most people don’t want a nixos-rebuild switch to set their speaker volume or display brightness (which it thankfully doesn’t). I use Home Manage to track my terminal configuration, but,I like changing the colour scheme of my terminal often. For me, terminal colour scheme should be a stateful value, because I change it on moods. However, I don’t know of a way to separate that out from the other terminal configuration that I do want to be in my Nix configuration.

Issue 3:
This is related to issue 2, but still distinct I believe. Nix rebuilds are kind of slow. I don’t have a problem with this if I’m editing things like my flake architecture or something else I don’t change often, but for something basic like installing a package, waiting for the NIx language interpreter to comb through every line of my config is kind of unnecessary. It would be nice if changing some piece of data like adding a package would not trigger a re-evaluation of all the Nix code.

My Solution:
I think it would be cool to decouple the logic behind reproducible builds, dependency handling, inheritance, etc. from the “meat” of the configuration: package lists and configuration options. For example, I would be able to declare inputs and outputs etc. in a language that compiles slowly, but is reliable and performant (like Rust or Go) and have them access data from a set of standardized configuration files (like YML or TOML). Nerds like us could edit the basic architecture, and all average Linux users would have to do to install and configure packages is to edit some familiar looking config files.

I realize this is likely extremely difficult to accomplish, but I think it is at least an interesting idea. That said, Nix and Guix both chose to use DSL programming languages rather than passive config files, so there is likely some huge drawback staring me in the face that I didn’t think of. I would love to hear the community’s thoughts.

Also, I saw somewhere that you can import stuff from YML etc. into a Nix file. How does that work exactly?


I agree, in general, that decoupling is almost always a good idea, but in this case the root of the issue might just be the performance of the Nix module system.

Do to the way the fixed point currently works, the more modules you add to an evaluation, the slower the evaluation becomes. Of course decoupling could help here if we had various pieces of a NixOS system separated into module groups that get updated independantly. Say if packages were entirely separate from systemd services and could be managed and updated independently from one another, similar to how NixOS and home-manager are separate now.

This would allow the evaluations for the individual parts to be a little faster, assuming they load less modules than an entire NixOS system currently does. Something like that, or just completely replacing the module system all together with something more performant, but the issue would be that we’d then have to throw away years and years of domain specific knowledge that has been built up in the NixOS modules in nixpkgs up til now.

It might be possible, perhaps with some modifications to the underlying C++ implementation, to potentially improve the performance of the module system at a lower level, say with some new builtin that uses C++ primitives to make the expensive recursion segments a little cheaper.

So yeah, it’s possible, and decoupling may actually be a nice idea at some point, but it’ll take a lot of work no matter which direction we try to go.

stateful config

As for having stateful configuration segments mixed with the stateless bits, it all depends on the tool at hand, but many tools allow you to import configuration from other locations. For example, you want a stateful place to update your shell theme, you can simply have a bit of shell code to optionally source another shell file from a writable location if it exists. This writable shell file could then set your theme, or override a stateless default (however you wanna do it).

yaml importer

I’m not sure if I’ve seen a native YAML importer, but there might be one somewhere. I know there is builtins.fromJSON and builtins.fromTOML.

1 Like

Over the years I’ve grown a bit of a philosophy around the theme you’re discussing:

  • It’s a reasonable decision that the Nix language is Turing-complete, because that naturally allows expressing complex things concisely. And, oh boy, software out there is complex.
  • Strings with context and first-class interpolation are the distinguishing feature that makes the Nix language domain-specific for file system data wrangling.
  • Breaking complicated things down to simple interfaces for regular people is sometimes possible, but the most expensive thing in software development, especially if one wants to maintain long-term backwards compatibility. I deem it largely unattainable for a community with a toy budget (relative to its size and scope).
  • Programmability is the driver behind customisation and decentralised evolution. The Nix ecosystem features all the benefits and drawbacks of The Lisp Curse.
  • One major reason why Nix is still niche is – apart from documentation still being more gaps than substance – the computational mindset required to make good use of it. I see no way around that without sacrificing the essence.

Maybe to put it with tongue in cheek: It’s not for everyone. (But one can grow into it, given enough time and a particular attitude.)