How should I write a function that contains an import?

In my NixOS flake.nix, I have

  outputs =
  { self, nixpkgs, home-manager, ... }@inputs:
  {
    nixosConfigurations = {
      foo = nixpkgs.lib.nixosSystem {
        system = "aarch64-linux";
        modules = [
          ./hosts/forge/default.nix

          home-manager.nixosModules.home-manager
          {
            home-manager.useGlobalPkgs = true;
            home-manager.useUserPackages = true;
            home-manager.users.vulcan = import ./shared/desktop/home.nix;
          }
        ];
      };
      bar = nixpkgs.lib.nixosSystem {
        system = "aarch64-linux";
        modules = [
          ./hosts/snowpi/default.nix

          home-manager.nixosModules.home-manager
          {
            home-manager.useGlobalPkgs = true;
            home-manager.useUserPackages = true;
            home-manager.users.vulcan = import ./shared/home.nix;
          }
        ];
      };
    };
  };

This works! But I want to deduplicate the repeated home-manager section, so I tried the following:

  outputs =
  { self, nixpkgs, home-manager, ... }@inputs:
  let
    hm =
      hmc: home-manager.nixosModules.home-manager
      {
        home-manager.useGlobalPkgs = true;
        home-manager.useUserPackages = true;
        home-manager.users.vulcan = import hmc;
      };
  in
  {
    nixosConfigurations = {
      foo = nixpkgs.lib.nixosSystem {
        system = "aarch64-linux";
        modules = [
          ./hosts/forge/default.nix

          (hm ./shared/desktop/home.nix)
        ];
      };
      bar = nixpkgs.lib.nixosSystem {
        system = "aarch64-linux";
        modules = [
          ./hosts/snowpi/default.nix

          (hm ./shared/home.nix)
        ];
      };
    };
  };

But I get an error when rebuilding (nixos-rebuild switch --flake .#snowpi):

error: function 'anonymous lambda' called without required argument 'lib'

       at /nix/store/rnq30155xjy9s0w82z8p3wvs1nb46ln6-source/nixos/default.nix:1:1:

            1| { config, lib, pkgs, utils, ... }:
             | ^
            2|

(I also tried importing inside nixosConfigurations, instead (hm (import ./shared/home.nix)), and got the same error.)

What does this error mean?

I’m not sure why that doesn’t work, but I vaguely recall something weird going on in home manager’s users. From memory I think the solution was to add an extra lambda around the import hmc, so replace it with args: import hmc args.

If that doesn’t work you can also try to replace the import hmc with { Imports = [hmc]; }.

The problem is that you included home-manager.nixosModules.home-manager in the function here, it should be this instead:

    hm =
      hmc:
      {
        home-manager.useGlobalPkgs = true;
        home-manager.useUserPackages = true;
        home-manager.users.vulcan = import hmc;
      };

Whereas the home-manager.nixosModules.home-manager part should stay in modules :slight_smile:

1 Like

@Infinisil This works! But I don’t understand why…
@olmokramer I get the same error when I try those two methods.

Ah yeah I missed the error that @Infinisil pointed out.

The module system is a higher-level abstraction based on the concept of modules: Nix values that declare and define options in a shared namespace. Modules can come in many forms, like attribute sets ({ ... }), functions ({ ... }: { ... }) and files containing attributes sets of functions. Modules aren’t composed by applying one to the other, but rather by passing multiple of them to some function that combines them together: nixosSystem here, but lib.evalModules is the primary function underneath. Ordering of modules notably mostly shouldn’t matter.

In your case, home-manager.nixosModules.home-manager is a binding that points to a module that declares the home-manager option, defining some NixOS options in turn, and { home-manager.useGlobalPkgs = true; ... } is a module that defines the home-manager option.

1 Like

To be extra clear, it’s important to realize what this syntax is doing. It looks like you’re calling home-manager.nixosModules.home-manager as a function, but you’re not. The home-manager.nixosModules.home-manager and the { ... } are each different list elements, because nix uses spaces to delimit lists. Which is infuriating because it also uses spaces for function application :stuck_out_tongue:

2 Likes

@Infinisil Thanks for the explanation!
@ElvishJerricco I definitely misread this as function application. Thanks!