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 that implictly takes a context parameter and forwards it to any function registered at
.includes.
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
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.
IMPORTANT:
parametric.exactlyonly dispatches to.includesfunctions. It will not take care of owned configurations. Seeparametric.withOwn.
The parametric.atLeast Functor
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.
IMPORTANT:
parametric.atLeastonly dispatches to.includesfunctions. It will not take care of owned configurations. Seeparametric.withOwn.
The Definition of den.default
The complete definition of den.default is as follows:
den.default = den.lib.parametric.atLeast { };
This means that den.default aspect is nothing more than a dispatcher that will include the results from forwarding its given context to all .includes functions.
The parametric.withOwn combinator.
The parametric function itself is an alias for parametric.withOwn parametric.atLeast.
Both parametric.atLeast and parametric.exactly only dispatch to functions. However, the parametric.withOwn combinator allows a parametric aspect to also include its owned configurations 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:
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 it will still only invoke a.
replaced = parametric.fixedTo { x = 1; } original;
# when used, it will call both a and b, { x = 0, y = 2 }
expanded = parametric.expands { y = 2; } original;
Advanced Context Handling
Note that since all den.lib.parametric combinators are themselves functors, the following two are exactly the same:
# recommended 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.