Unverified Commit b4292c43 authored by Robert Hensing's avatar Robert Hensing Committed by GitHub
Browse files

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

parents 537289ee b8c8cbbb
Loading
Loading
Loading
Loading
+91 −47
Original line number Diff line number Diff line
@@ -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

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

    : 4\. Function argument
  */
  composeExtensions =
    f: g: final: prev:
      let fApplied = f final prev;
          prev' = prev // fApplied;
      in fApplied // g final 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; };

  /**
    Compose several extending functions of the type expected by 'extends' into
    one where changes made in preceding functions are made available to
    subsequent ones.
      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 : [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) // {
  makeExtensibleWithCustomName =
    extenderName: rattrs:
    fix' (
      self:
      (rattrs self)
      // {
        ${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
    });
      }
    );
}