Context-Aware Functional Aspects
Functors are how Den achieves context adaptation and configuration parameterization.
Be sure to read the section about Functors.
A Parametric aspect is one whose sole purpose is to dispatch to its
.includesfunctions based on an existing context.
You create a parametric aspect by using one of the den.lib.parametric functors detailed below.
The most common and frequenly used is the den.lib.parametric function itself, it expects an non-functional aspect as an attribute-set and creates a parametric aspect from it.
den.aspects.foo = den.lib.parametric {
nixos.foo = 1;
includes = [ den.aspects.bar ];
};
The foo aspect will be able to take any type of context (argument), for example it can be applied with foo { x = 1; } and foo will forward the same context { x } to bar or any other of its .includes as long as they can take atLeast the same argument names.
The parametric.exactly Functor
IMPORTANT:
parametric.exactlyonly dispatches to.includesfunctions. It will not take care of owned configurations. For that useparametricfunction itself, or(parametric.withOwn parametric.exactly).
For example:
let
a = { x, ... }: { nixos.foo = x; };
b = { x, y }: { nixos.foo = y; };
c = { z }: { nixos.foo = z; };
F = parametric.exactly {
includes = [ a b c ];
};
in F
When the F aspect is applied like F { x = 0; y = 1; }, it will only invoke the b function, since it is the ONLY function that takes exactly the named arguments { x, y }, no more, no less.
The parametric.atLeast Functor
IMPORTANT:
parametric.atLeastonly dispatches to.includesfunctions. It will not take care of owned configurations. For that useparametricfunction itself, or(parametric.withOwn parametric.atLeast).
Using the same example as before:
let
a = { x, ... }: { nixos.foo = x; };
b = { x, y }: { nixos.foo = y; };
c = { z }: { nixos.foo = z; };
F = parametric.atLeast {
includes = [ a b c ];
};
in F
Calling F { x = 1; y = 2; } will invoke a and b, including both of their results. In this example, this causes different values of nixos.foo to be set.
The Definition of den.default
The complete definition of den.default is as follows:
den.default = den.lib.parametric.atLeast { };
This means that the den.default aspect is nothing more than a router that will include results from its .includes functions according to the current context.
The parametric.withOwn combinator.
The parametric function itself is an alias for parametric.withOwn parametric.atLeast.
Meaning that it will behave like parametric.atLeast but it will
also include the aspect owned modules and static includes.
Fixed-Context Aspects and Context Adaptation
Context adaptation refers to changing the context of an existing aspect, making it provide a different set of modules.
An aspect calling parametric.fixedTo with an attribute set becomes a fixed or known-context aspect and can be included as if it were a static aspect without having to call it as a function.
The parametric.expands functor allows you to append an attribute set to whatever context is received at the call.
This example adapts an aspect by replacing its inner __functor twice:
let
a = { x, ... }: { nixos.foo = x; };
b = { x, y, ... }: { nixos.foo = y; };
# when original is used, it will only call a since b needs more context
original = parametric.fixedTo { x = 0; } {
includes = [ a b ];
};
# replaces the context, but updated will still only invoke a.
updated = parametric.fixedTo { x = 1; } original;
# when adapted is used, it will call both a and b, { x = 0, y = 2 }
adapted = parametric.expands { y = 2; } original;
in adapted
Advanced Context Handling
Note that since all den.lib.parametric combinators are themselves functors, the following two are exactly the same:
# functional style: applying to an aspect.
parametric.atLeast {
includes = [ a b ];
};
# attribute style: setting __functor attribute.
{
includes = [ a b ];
__functor = parametric.atLeast;
}
The first alternative is the recommended use, since it is more idiomatic and does not
surfaces knowledge about the internal __functor.
However, for more advanced aspects, you can provide your own __functor attribute.
Read the parametric code for how to build your own.
Also, reading the tests for these parametric functors can be of help.