doc: lib.composeExtensions reference to overlays (#325479)

This commit is contained in:
Robert Hensing 2024-09-13 11:39:24 +02:00 committed by GitHub
commit b4292c4370
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -63,7 +63,6 @@ rec {
See [`extends`](#function-library-lib.fixedPoints.extends) for an example use case. See [`extends`](#function-library-lib.fixedPoints.extends) for an example use case.
There `self` is also often called `final`. There `self` is also often called `final`.
# Inputs # Inputs
`f` `f`
@ -90,7 +89,12 @@ rec {
::: :::
*/ */
fix = f: let x = f x; in x; fix =
f:
let
x = f x;
in
x;
/** /**
A variant of `fix` that records the original recursive attribute set in the A variant of `fix` that records the original recursive attribute set in the
@ -99,14 +103,20 @@ rec {
This is useful in combination with the `extends` function to This is useful in combination with the `extends` function to
implement deep overriding. implement deep overriding.
# Inputs # Inputs
`f` `f`
: 1\. Function argument : 1\. Function argument
*/ */
fix' = f: let x = f x // { __unfix__ = f; }; in x; fix' =
f:
let
x = f x // {
__unfix__ = f;
};
in
x;
/** /**
Return the fixpoint that `f` converges to when called iteratively, starting Return the fixpoint that `f` converges to when called iteratively, starting
@ -117,7 +127,6 @@ rec {
0 0
``` ```
# Inputs # Inputs
`f` `f`
@ -134,13 +143,12 @@ rec {
(a -> a) -> a -> a (a -> a) -> a -> a
``` ```
*/ */
converge = f: x: converge =
f: x:
let let
x' = f x; x' = f x;
in in
if x' == x if x' == x then x else converge f x';
then x
else converge f x';
/** /**
Extend a function using an overlay. Extend a function using an overlay.
@ -149,7 +157,6 @@ rec {
A fixed-point function is a function which is intended to be evaluated by passing the result of itself as the argument. A fixed-point function is a function which is intended to be evaluated by passing the result of itself as the argument.
This is possible due to Nix's lazy evaluation. This is possible due to Nix's lazy evaluation.
A fixed-point function returning an attribute set has the form A fixed-point function returning an attribute set has the form
```nix ```nix
@ -257,7 +264,6 @@ rec {
``` ```
::: :::
# Inputs # Inputs
`overlay` `overlay`
@ -299,8 +305,7 @@ rec {
::: :::
*/ */
extends = extends =
overlay: overlay: f:
f:
# The result should be thought of as a function, the argument of that function is not an argument to `extends` itself # The result should be thought of as a function, the argument of that function is not an argument to `extends` itself
( (
final: final:
@ -311,63 +316,98 @@ rec {
); );
/** /**
Compose two extending functions of the type expected by 'extends' Compose two overlay functions and return a single overlay function that combines them.
into one where changes made in the first are available in the For more details see: [composeManyExtensions](#function-library-lib.fixedPoints.composeManyExtensions).
'super' of the second
# Inputs
`f`
: 1\. Function argument
`g`
: 2\. Function argument
`final`
: 3\. Function argument
`prev`
: 4\. Function argument
*/ */
composeExtensions = composeExtensions =
f: g: final: prev: f: g: final: prev:
let fApplied = f final prev; let
prev' = prev // fApplied; fApplied = f final prev;
in fApplied // g final prev'; prev' = prev // fApplied;
in
fApplied // g final prev';
/** /**
Compose several extending functions of the type expected by 'extends' into Composes a list of [`overlays`](#chap-overlays) and returns a single overlay function that combines them.
one where changes made in preceding functions are made available to
subsequent ones. :::{.note}
The result is produced by using the update operator `//`.
This means nested values of previous overlays are not merged recursively.
In other words, previously defined attributes are replaced, ignoring the previous value, unless referenced by the overlay; for example `final: prev: { foo = final.foo + 1; }`.
:::
# Inputs
`extensions`
: A list of overlay functions
:::{.note}
The order of the overlays in the list is important.
:::
: Each overlay function takes two arguments, by convention `final` and `prev`, and returns an attribute set.
- `final` is the result of the fixed-point function, with all overlays applied.
- `prev` is the result of the previous overlay function(s).
# Type
``` ```
composeManyExtensions : [packageSet -> packageSet -> packageSet] -> packageSet -> packageSet -> packageSet # Pseudo code
^final ^prev ^overrides ^final ^prev ^overrides let
# final prev
# ↓ ↓
OverlayFn = { ... } -> { ... } -> { ... };
in
composeManyExtensions :: ListOf OverlayFn -> OverlayFn
``` ```
# Examples
:::{.example}
## `lib.fixedPoints.composeManyExtensions` usage example
```nix
let
# The "original function" that is extended by the overlays.
# Note that it doesn't have prev: as argument since no overlay function precedes it.
original = final: { a = 1; };
# Each overlay function has 'final' and 'prev' as arguments.
overlayA = final: prev: { b = final.c; c = 3; };
overlayB = final: prev: { c = 10; x = prev.c or 5; };
extensions = composeManyExtensions [ overlayA overlayB ];
# Caluculate the fixed point of all composed overlays.
fixedpoint = lib.fix (lib.extends extensions original );
in fixedpoint
=>
{
a = 1;
b = 10;
c = 10;
x = 3;
}
```
:::
*/ */
composeManyExtensions = composeManyExtensions = lib.foldr (x: y: composeExtensions x y) (final: prev: { });
lib.foldr (x: y: composeExtensions x y) (final: prev: {});
/** /**
Create an overridable, recursive attribute set. For example: Create an overridable, recursive attribute set. For example:
``` ```
nix-repl> obj = makeExtensible (self: { }) nix-repl> obj = makeExtensible (final: { })
nix-repl> obj nix-repl> obj
{ __unfix__ = «lambda»; extend = «lambda»; } { __unfix__ = «lambda»; extend = «lambda»; }
nix-repl> obj = obj.extend (self: super: { foo = "foo"; }) nix-repl> obj = obj.extend (final: prev: { foo = "foo"; })
nix-repl> obj nix-repl> obj
{ __unfix__ = «lambda»; extend = «lambda»; foo = "foo"; } { __unfix__ = «lambda»; extend = «lambda»; foo = "foo"; }
nix-repl> obj = obj.extend (self: super: { foo = super.foo + " + "; bar = "bar"; foobar = self.foo + self.bar; }) nix-repl> obj = obj.extend (final: prev: { foo = prev.foo + " + "; bar = "bar"; foobar = final.foo + final.bar; })
nix-repl> obj nix-repl> obj
{ __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; } { __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; }
@ -379,7 +419,6 @@ rec {
Same as `makeExtensible` but the name of the extending attribute is Same as `makeExtensible` but the name of the extending attribute is
customized. customized.
# Inputs # Inputs
`extenderName` `extenderName`
@ -390,8 +429,13 @@ rec {
: 2\. Function argument : 2\. Function argument
*/ */
makeExtensibleWithCustomName = extenderName: rattrs: makeExtensibleWithCustomName =
fix' (self: (rattrs self) // { extenderName: rattrs:
${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs); fix' (
}); self:
(rattrs self)
// {
${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
}
);
} }