NixOS / nix

Nix, the purely functional package manager
https://nixos.org/
GNU Lesser General Public License v2.1
12.77k stars 1.52k forks source link

Add some Python comprehensions as syntax for attrset generation #2543

Open danbst opened 5 years ago

danbst commented 5 years ago

To create a dictionary from list I do like this:

{ lib }:
lib.genAttrs [ "foo" "bar" ] (name: "x_" + name)

This is not nice. What about this:

{
  for name in [ "foo" "bar" ];
    ${name} = "x_${name}";
}

and this

let source = { a = "foo"; b = "bar"; };
in {
  for { name, value } in source;
    ${name} = "x_${name}";
  for { name, value } in source;
    "${name}_value" = "x_${value}";
}

Essentially this will be syntax sugar for inherit:

{ lib }:
{
  inherit (lib.genAttrs [ "foo" "bar" ] (
    name: "x_" + name
  ));
}
danbst commented 5 years ago

Another example where this would be nice. There is code in zram module, which looks like:

    systemd.services =
      let
        createZramInitService = dev:
          nameValuePair "zram-init-${dev}" {
            description = "Init swap on zram-based device ${dev}";
            bindsTo = [ "dev-${dev}.swap" ];
            after = [ "dev-${dev}.device" "zram-reloader.service" ];
            requires = [ "dev-${dev}.device" "zram-reloader.service" ];
            # ...
          };
      in listToAttrs ((map createZramInitService devices) ++ [(nameValuePair "zram-reloader"
        {
          description = "Reload zram kernel module when number of devices changes";
          serviceConfig = {
            # ...
          };
          restartTriggers = [ cfg.numDevices ];
          restartIfChanged = true;
        })]);

It's functional style is a bit mess. When rewriting in comprehension style, it will look like:

    systemd.services = {
      for dev in devices;
        "zram-init-${dev}" = {
            description = "Init swap on zram-based device ${dev}";
            bindsTo = [ "dev-${dev}.swap" ];
            after = [ "dev-${dev}.device" "zram-reloader.service" ];
            requires = [ "dev-${dev}.device" "zram-reloader.service" ];
            # ...
        };
      zram-reloader = {
          description = "Reload zram kernel module when number of devices changes";
          serviceConfig = {
            # ...
          };
          restartTriggers = [ cfg.numDevices ];
          restartIfChanged = true;
      };
    };

This comprehension solution is more natural than listToAttrs, because doesn't require namevalue pair concept and builtins.listToAttrs is trivially implemented with comprehension syntax:

    listToAttrs = l: {
      for x in l; ${x.name} = x.value;
    };

cc @edolstra

danbst commented 5 years ago

@volth can you show the desired syntax, how ZRAM example would look like?

edolstra commented 5 years ago

nameValuePair predates dynamic attributes.

stale[bot] commented 3 years ago

I marked this as stale due to inactivity. → More info

stale[bot] commented 2 years ago

I closed this issue due to inactivity. → More info