Why sets cannot merge because of their orders of appearance?

foo is an option of types.unspecified.

nix-repl> {foo = { a = {d = [ "a" ]; h = "afd"; }; c= 3; }; foo.a.b = 2; }.foo.a.b
2

nix-repl> {foo = { a = {d = [ "a" ]; h = "afd"; }; c= 3; }; foo.a.b = 2; }.foo.a.d
[ "a" ]

---

nix-repl> {foo.a.b = 2; foo = { a = {d = [ "a" ]; h = "afd"; }; c= 3; }; }.foo.a.d 
error: attribute 'a' at (string):1:2 already defined at (string):1:23

Why simple swap of order here would not make sets merge together properly? How do I write so that they could arbitatrily merge without problems from orders of appearances?

The ultimate goal here is to create an option that could store a deep, nested set which is defined separately in multiple files in different ways like

// file 1
foo.bar.h = 1;

// file 2
foo = { bar = { a = [ "a" ]; }; baz = 2; };
lib.recursiveUpdate {
  foo.bar.h = 1;
} {
  foo = { bar = { a = [ "a" ]; }; baz = 2; };
}
# => { foo = { bar = { a = [ "a" ]; h = 1; }; baz = 2; }; }

for files you could do something like:

builtins.foldl' lib.recursiveUpdate {} (map import [ ./a.nix ./b.nix ])
{ foo = { bar = { a = [ "a" ]; h = 1; }; baz = 2; }; }

You’ll still have to be careful about conflicts, so you can easily override whole trees if your last definition would be { foo = 1; } for example.
That’s what the NixOS module system tries to alleviate, for simpler things I’ve been using YANTS.

1 Like

But why would Nix not automatically merge it “properly”. I still don’t see the reason for it to fail by just swap two statements.

Is that because the option is unspecified so that Nix would not know how to merge it? But also I don’t know how to express properly the type for a infinite nested attrset.