Mapping attribute sets to option of type "list of submodules"

I’ve got the following configuration for my monitors:

mochi.device.display.monitors = [
  {
    name = "DP-3";
    mode = "1920x1080@144Hz";
    position = "0,0";
    background = "${config.home.homeDirectory}/.config/mochi/static/media/main.jpg fill";
  }
  {
    name = "HDMI-A-1";
    mode = "1920x1080";
    transform = "90";
    position = "-1080,-420";
    background = "${config.home.homeDirectory}/.config/mochi/static/media/side.png fill";
  }
];

The options are defined as follows:

options.mochi.device.display = {
  monitors = mkOption {
    type = types.listOf (types.submodule {
      options = {
        name = mkOption {
          type = types.str;
        };
        mode = mkOption {
          type = types.nullOr types.str;
          default = null;
        };
        position = mkOption {
          type = types.nullOr types.str;
          default = null;
        };
        transform = mkOption {
          type = types.nullOr types.str;
          default = null;
        };
        background = mkOption {
          type = types.nullOr types.str;
          default = null;
        };
      };
    });
  };
};

I’ve then got the following configuration for Kanshi:

{ config, ... }:

let
  cfg = config.mochi;

in
{
  services.kanshi = {
    enable = true;
    settings = [
      {
        profile.name = "main";
        profile.outputs = builtins.map(monitor: {
          criteria = monitor.criteria;
          mode = monitor.mode;
          position = monitor.position;
          transform = monitor.transform;
        } cfg.device.display.monitors);
      }
    ];
  };
}

However, I get the following error:

warning: Git tree '/etc/nixos' is dirty
building the system configuration...
warning: Git tree '/etc/nixos' is dirty
error:
       … while calling the 'head' builtin
         at /nix/store/41119f50cmp7wrc02jmkm8mlh34p0cfx-source/lib/attrsets.nix:1571:13:
         1570|           if length values == 1 || pred here (elemAt values 1) (head values) then
         1571|             head values
             |             ^
         1572|           else

       … while evaluating the attribute 'value'
         at /nix/store/41119f50cmp7wrc02jmkm8mlh34p0cfx-source/lib/modules.nix:1083:7:
         1082|     // {
         1083|       value = addErrorContext "while evaluating the option `${showOption loc}':" value;
             |       ^
         1084|       inherit (res.defsFinal') highestPrio;

       … while evaluating the option `system.build.toplevel':

       … while evaluating definitions from `/nix/store/41119f50cmp7wrc02jmkm8mlh34p0cfx-source/nixos/modules/system/activation/top-level.nix':

       … while evaluating the option `home-manager.users.aidan.services.kanshi.settings."[definition 1-entry 1]".profile.outputs':

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: A definition for option `home-manager.users.aidan.services.kanshi.settings."[definition 1-entry 1]".profile.outputs' is not of type `list of (submodule)'. Definition values:
       - In `/nix/store/4kjdw1bxhn7089vp47bzvz5dk5mwd0v0-source/programs/individual/kanshi': <function>

When using builtins.map, how can I make an attribute set into an instance of a submodule?

This is just a simplesyntax error!

Took me a moment too but it’s easy if you take the error message literally.

The closing parenthesis of the second argument includes what is supposed the last argument. Thus, the value supplied to the option is the partially applied map function (one argument instead of two) instead of its return value (which would be listOf submodule).

I’d recommend you try to follow the error message yourself armed with this knowledge to understand how this message relates to your actual error such that you can figure this out yourself the next time. :slight_smile:

Though I don’t quite understand why you need to map here in the first place?
Also look into the inherit syntax for situations like what you did in the lambda.

1 Like

Thank you so much, this was the issue :smile: I’ve switched to using inherit for (monitor) mode position transform. Otherwise, I made a mistake in my code: criteria = monitor.criteria; should be criteria = monitor.name. I wanted to change the name of the one property there to fit Kanshi’s submodule.