How to extend lists from the same file?

Suppose a file looking something like this:

{
    list = [ "foo" "bar" ];
    # a bunch
    # of other 
    # code
    list = list ++ [ "foobar" ];
}

This won’t work, because list is defined twice.
So my question is:
From the same .nix file, is it possible to define a list in a way, that multiple values are declared at different locations in my file?

My usecase is that i want to have my configuration.nix split into sections like this:

{ config, lib, pkgs, ... }:
{
    # section one
    path.to.option1 = "foo";
    environment.systemPackages = with pkgs; [ foo ];
    # section two
    path.to.option2 = "bar";
    environment.systemPackages = with pkgs; [ bar ];
}

This works if I import a separate module for every section, but that is not what i want.

i could probably write a function that turns an attribute set like this:

{
    o1 = [ "foo" "bar" ];
    o2 = [ "foobar" ]; 
}

to the list

[ "foo" "bar" "foobar" ]

and then declare my list like this:

let
    # edit: it is actually really simple lol
    concatListsInAttrs = attrs: builtins.concatLists (builtins.attrValues attrs);
in
{
    list = concatListsInAttrs listAttrs;
    listAttrs.foo = [ "foo" "bar" ];
    # a bunch
    # of other
    # code
    listAttrs.foobar = [ "foobar" ];
}

but idk that seems pretty hacky
i’d still like to have a better solution

This is not a Nix language feature, but rather a module system feature:

{ config, lib, pkgs, ... }:
lib.mkMerge [
  {
    # section one
    path.to.option1 = "foo";
    environment.systemPackages = with pkgs; [ foo ];
  }
  {
    # section two
    path.to.option2 = "bar";
    environment.systemPackages = with pkgs; [ bar ];
  }
]
1 Like

Oh and alternatively, just using additional inline modules:

{ config, lib, pkgs, ... }: {
  imports = [
    {
      # section one
      path.to.option1 = "foo";
      environment.systemPackages = with pkgs; [ foo ];
    }
    {
      # section two
      path.to.option2 = "bar";
      environment.systemPackages = with pkgs; [ bar ];
    }
  ];
}
2 Likes

shouldn’t it be

{ config, lib, pkgs, ... }: {
  imports = [
    ({ config, lib, pkgs }: {
      # section one
      path.to.option1 = "foo";
      environment.systemPackages = with pkgs; [ foo ];
    })
    ({ config, lib, pkgs }: {
      # section two
      path.to.option2 = "bar";
      environment.systemPackages = with pkgs; [ bar ];
    })
  ];
}

or do inline modules not have to be functions?

Modules in general don’t need to be functions! E.g. this also works:

{
  imports = [
    {}
    {}
  ];
}

You could even use both mkMerge and imports at the same time:

{ lib, ... }: {
  imports = [
    # These are full modules, they can define options, more `imports`, etc.
    {}
    {}
  ];
  config = lib.mkMerge [
    # These are only `config`, you can define options, but not declare additional ones or import other modules
    {}
    {}
  ];
}
2 Likes