users.users = let
users = [ "one" "two"];
in builtins.listToAttrs (map (user: lib.nameValuePair user {
isNormalUser = true;
}) users);
Nix doesn’t have any syntactic sugar for unrolling a list and applying a function to it. You need to tell it to do that. This is a design decision - personally I think this usually makes the language more comfortable to read because you need to know less, but opinions can differ.
“${var}” just takes the value of var and puts it in the string, and if var is not a string nix complains (so you need to toString it).
FWIW, I tend to find the following construction easier to read because of the different order of arguments, and because the thing being constructed is more visibly an attrset element.
users.users =
let
inherit (lib.lists) foldl forEach;
users = [ "one" "two" ];
in
foldl (a: b: a // b) { } (
forEach users (user: {
user = {
isNormalUser = true;
};
})
);
They’re doing exactly the same thing: making a list of single-element attrsets, then merging them.
The advantages if this form are even more apparent when there are multiple nested loops to iterate over; you just throw a flatten around the loops.
I often hear about how *nix is not well documented and personally, i think that this is not the case.
Nonetheless, it appears to me that most of time i am truly learning something about nix is when reading through other peoples nix code like e.g. your examples or nixpkgs and trying to understand what’s happening. Well, that is what it is worth to me, at least. So thank you too, @uep.