Reuse attributes sets partly in different parts of the config

Hey everyone,

I’ve been using NixOS for a while now, but until now, I haven’t quite figured out how to remove duplication in my NixOS configuration. One specific problem that I have is, that I couldn’t figure out, yet, where it is possible to reuse specific attribute sets at a different point in the configuration, for example for different users.

My minimalized examples is this:

  home-manager.users.itsme.programs.firefox.profiles = {
    "profile1".settings = {
      "browser.aboutwelcome.enabled" = false;
      "browser.translations.enable" = false;
      "browser.startup.blankWindow" = true;
    };
    "profile2".settings = {
      "browser.aboutwelcome.enabled" = true;
      "browser.translations.enable" = false;
      "browser.startup.blankWindow" = true;
    };
  };

So I have to very similar attribute sets in different places in the configuration. My initial approach to reusing code would be something like this:

home-manager.users.itsme.programs.firefox.profiles = let
    general_settings = {
      "browser.aboutwelcome.enabled" = false;
      "browser.translations.enable" = false;
      "browser.startup.blankWindow" = true;
    };
  in {
    "profile1".settings = general_settings;
    "profile2".settings = general_settings;
    "profile2".settings = { "browser.aboutwelcome.enabled" = true; };
  };

Of course, this doesn’t work, because “profile2”.settings cannot be defined multiple times. Is there any way in nix to reuse attribute sets partly?

I have tried to search documentation and forum, but couldn’t find anything that worked. Feel free to point me to other forum threads or documentation if I missed anything. I would really appreciate your help :slight_smile:

Nix allows to merge AttrSets using the // operator. For your example, this could look like the following:

home-manager.users.itsme.programs.firefox.profiles = let
    general_settings = {
      "browser.aboutwelcome.enabled" = false;
      "browser.translations.enable" = false;
      "browser.startup.blankWindow" = true;
    };
  in {
    "profile1".settings = general_settings;
    "profile2".settings = general_settings // {
      "browser.aboutwelcome.enabled" = true;
    };
  };

Keep in mind that this overrides existing attributes on the ‘top-level’, for recursive merging you probably want to use something like lib.attrsets.recursiveUpdate.

1 Like

// is the update operator and does no merging.
As I’ve mentioned elsewhere, use lib.mkMerge (not // nor lib.recursiveUpdate) in the module system, if you want merging behaviour.

3 Likes

Thanks for the correction/clarification with regard to mkMerge. That’s a good point.

Interestingly, the nix reference manual may be considered slightly misleading as it talks about “merging two sets” here with regard to the //-operator. (It’s still called update operator elsewhere) :thinking:

Operators - Nix Reference Manual, the manual is updated on that on newer nix versions, 2.18 still had it wrong though.

1 Like

That’s a different page, 2.18 has it right on that page: Operators - Nix Reference Manual

And 2.24 still uses the “merge” terminology in Nix Language - Nix Reference Manual

One could make the argument that it’s a “shallow merge” of attrsets, but given the preference for the second arg, I believe “update” is a clearer term. (And it’s certainly not the same as the merge in the module system, as features of the module system, like priority and merge strategy, are ignored.)

1 Like