Skip to content

Parametric Aspects

Den inspects function arguments at evaluation time using builtins.functionArgs. A function’s required parameters determine which contexts it matches:

# Matches: {host}, {host, user}, {host, user, foo}
({ host, ... }: { nixos.x = host.hostName; })
# Matches: {host, user} only
(den.lib.perUser ({ host, user }: { nixos.x = user.userName; }))
# Matches: {home} only
(den.lib.perHome ({ home }: { homeManager.x = home.userName; }))

den.lib.canTake (nix/fn-can-take.nix) checks if a function’s required arguments are satisfiable with given parameters:

canTake { host = ...; } ({ host, ... }: ...) # => true (atLeast)
canTake { host = ...; } ({ host, user }: ...) # => false (user missing)
canTake.exactly { host = ...; } ({ host }: ...) # => true
canTake.exactly { host = ...; foo = ...; } ({ host, ... }: ...) # => false (extras)
  • canTake.atLeast — all required params are present (default).
  • canTake.exactly — required params match provided params exactly.

den.lib.take wraps canTake into aspect-ready helpers:

# Apply fn only if ctx satisfies atLeast
den.lib.take.atLeast fn ctx
# Apply fn only if ctx satisfies exactly
den.lib.take.exactly fn ctx

These are used inside includes to control when a function activates.

den.lib.parametric changes an aspect __functor which processes its includes list through parametric dispatch:

den.lib.parametric {
nixos.foo = 1; # owned config, always included
includes = [
{ nixos.bar = 2; } # static, always included
({ host }: { nixos.x = host.name; }) # parametric, host contexts only
({ user }: { homeManager.y = 1; }) # parametric, user contexts only
];
}

When the aspect’s __functor is called with a context, it filters includes based on argument compatibility and returns only matching entries.

ConstructorBehavior
parametricDefault.
Includes owned classes + static includes + function includes with context matching atLeast.
parametric.atLeastDoes NOT include owned classes nor static includes. Only function matching atLeast the context.
parametric.exactlyLike atLeast but using canTake.exactly for match.
parametric.fixedTo attrsLike parametric default but ignores any context and always uses the given attrs .
parametric.expands attrsLike parametric, but extends the received context with attrs before dispatch.

This is a pattern used by many of our built-in batteries, be sure to see their code as example.

den.lib.parametric.exactly {
includes = [
({ user, host }: { ... })
({ home }: { ... })
];
};

The first function runs for every {host, user} context. The second only runs for {home} (standalone home-manager) contexts. No mkIf needed.

Contribute Community Sponsor