diff --git a/lib/fixed-points.nix b/lib/fixed-points.nix index 2a31b44f27c17b2..c8d2d1f03acbef9 100644 --- a/lib/fixed-points.nix +++ b/lib/fixed-points.nix @@ -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` @@ -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 @@ -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 @@ -117,7 +127,6 @@ rec { 0 ``` - # Inputs `f` @@ -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. @@ -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 @@ -257,7 +264,6 @@ rec { ``` ::: - # Inputs `overlay` @@ -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: @@ -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 = + 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. + 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"; } @@ -379,7 +419,6 @@ rec { Same as `makeExtensible` but the name of the extending attribute is customized. - # Inputs `extenderName` @@ -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); + } + ); }