Skip to content

Parametric Aspects

Providers inside provides can be curried functions. The outer function takes user arguments; the inner function (if needed) takes { class, aspect-chain }:

flake.aspects = { aspects, ... }: {
base.provides.user = userName: {
nixos.users.users.${userName}.isNormalUser = true;
};
server = {
includes = [ (aspects.base._.user "bob") ];
nixos = { };
};
};

Resolving server for "nixos" produces imports containing { users.users.bob.isNormalUser = true; }.

Top-level aspects can also be curried. The function takes named arguments and returns an aspect:

flake.aspects = { aspects, ... }: {
greeter = { message }: {
nixos.greeting = message;
};
host = {
includes = [ (aspects.greeter { message = "hello"; }) ];
nixos = { };
};
};

The type system (nix/types.nix) distinguishes between:

  • Direct providers: { class, aspect-chain } → aspect (1-2 named args: class and/or aspect-chain)
  • Curried providers: anything → provider (other function signatures)

A curried provider is invoked by the user at inclusion time. Its return value must be either another provider function or a direct aspect attrset.

A parametric provider can return a context-aware function:

provides.themed = theme:
{ class, aspect-chain }:
{ ${class}.theme = "${theme}-for-${(lib.last aspect-chain).name}"; };

This is a three-level chain: user args → context → aspect config.

Contribute Community Sponsor