Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: lib.composeExtensions reference to overlays #325479

Merged
merged 3 commits into from
Sep 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 91 additions & 47 deletions lib/fixed-points.nix
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ rec {
See [`extends`](#function-library-lib.fixedPoints.extends) for an example use case.
There `self` is also often called `final`.


# Inputs

`f`
Expand All @@ -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
Expand All @@ -99,14 +103,20 @@ rec {
This is useful in combination with the `extends` function to
implement deep overriding.


# Inputs

`f`

: 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
Expand All @@ -117,7 +127,6 @@ rec {
0
```


# Inputs

`f`
Expand All @@ -134,13 +143,12 @@ rec {
(a -> a) -> a -> a
```
*/
converge = f: x:
converge =
f: x:
let
x' = f x;
in
if x' == x
then x
else converge f x';
if x' == x then x else converge f x';

/**
Extend a function using an overlay.
Expand All @@ -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.
This is possible due to Nix's lazy evaluation.


A fixed-point function returning an attribute set has the form

```nix
Expand Down Expand Up @@ -257,7 +264,6 @@ rec {
```
:::


# Inputs

`overlay`
Expand Down Expand Up @@ -299,8 +305,7 @@ rec {
:::
*/
extends =
overlay:
f:
overlay: f:
# The result should be thought of as a function, the argument of that function is not an argument to `extends` itself
(
final:
Expand All @@ -311,63 +316,98 @@ rec {
);

/**
Compose two extending functions of the type expected by 'extends'
into one where changes made in the first are available in the
'super' of the second
Compose two overlay functions and return a single overlay function that combines them.
For more details see: [composeManyExtensions](#function-library-lib.fixedPoints.composeManyExtensions).
*/
composeExtensions =
hsjobeki marked this conversation as resolved.
Show resolved Hide resolved
f: g: final: prev:
let
fApplied = f final prev;
prev' = prev // fApplied;
in
fApplied // g final prev';

/**
Composes a list of [`overlays`](#chap-overlays) and returns a single overlay function that combines them.

:::{.note}
The result is produced by using the update operator `//`.
This means nested values of previous overlays are not merged recursively.
hsjobeki marked this conversation as resolved.
Show resolved Hide resolved
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

`f`
`extensions`

: 1\. Function argument
: A list of overlay functions
:::{.note}
The order of the overlays in the list is important.
:::

`g`
: 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).

: 2\. Function argument
# Type

`final`
```
# Pseudo code
let
# final prev
# ↓ ↓
OverlayFn = { ... } -> { ... } -> { ... };
in
composeManyExtensions :: ListOf OverlayFn -> OverlayFn
```

: 3\. Function argument
# 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; };

`prev`
# 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; };

: 4\. Function argument
*/
composeExtensions =
f: g: final: prev:
let fApplied = f final prev;
prev' = prev // fApplied;
in fApplied // g final prev';
extensions = composeManyExtensions [ overlayA overlayB ];

/**
Compose several extending functions of the type expected by 'extends' into
one where changes made in preceding functions are made available to
subsequent ones.
# 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 : [packageSet -> packageSet -> packageSet] -> packageSet -> packageSet -> packageSet
^final ^prev ^overrides ^final ^prev ^overrides
```
:::
*/
composeManyExtensions =
lib.foldr (x: y: composeExtensions x y) (final: prev: {});
composeManyExtensions = lib.foldr (x: y: composeExtensions x y) (final: prev: { });

/**
Create an overridable, recursive attribute set. For example:

```
nix-repl> obj = makeExtensible (self: { })
nix-repl> obj = makeExtensible (final: { })

nix-repl> obj
{ __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
{ __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
{ __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; }
Expand All @@ -379,7 +419,6 @@ rec {
Same as `makeExtensible` but the name of the extending attribute is
customized.


# Inputs

`extenderName`
Expand All @@ -390,8 +429,13 @@ rec {

: 2\. Function argument
*/
makeExtensibleWithCustomName = extenderName: rattrs:
fix' (self: (rattrs self) // {
${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
});
makeExtensibleWithCustomName =
extenderName: rattrs:
fix' (
self:
(rattrs self)
// {
${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
}
);
}