Skip to content

Providers & Fixpoint

Aspects can expose sub-aspects through the provides attribute. The _ attribute is a shorthand alias.

flake.aspects = { aspects, ... }: {
gaming = {
nixos = { };
provides.emulation = {
nixos = { };
_.nes.nixos = { }; # _ is alias for provides
};
};
my-host.includes = [ aspects.gaming._.emulation._.nes ];
};

Each entry in provides is itself an aspect — it has its own name, includes, class configs, and can nest further.

Providers that are functions receive { class, aspect-chain }:

provides.logging = { class, aspect-chain }: {
${class}.enableLogging = true;
};

Both the top-level flake.aspects and each provides submodule receive an aspects argument — a fixpoint of their sibling scope. This means:

  • Top-level aspects can reference each other: aspects.foo, aspects.bar.
  • Providers can reference siblings: aspects.sibling within the same provides block.
  • Providers can reference top-level aspects via closure.
flake.aspects = { aspects, ... }: {
two.provides = { aspects, ... }: {
sub = {
classOne = { };
includes = [ aspects.sibling ];
};
sibling.classOne = { };
};
five.classOne = { };
one.includes = [
aspects.two._.sub # reaches into two's providers
];
};

The fixpoint is implemented via freeformType with _module.args.aspects = config in types.nix, so aspects always refers to the evaluated sibling scope.

The type system (nix/types.nix) accepts several provider shapes:

ShapeDescription
Aspect attrsetDirect { classX = ...; includes = [...]; }
{ class, aspect-chain } → aspectContext-aware provider
{ class } → aspectProvider needing only class
{ aspect-chain } → aspectProvider needing only chain
args → providerCurried — parametric (see guide)

All of these can appear in includes or as provides entries. The type system distinguishes them via isProviderFn and isSubmoduleFn checks.

Contribute Community Sponsor