Unverified Commit 41298a0d authored by Valentin Gagarin's avatar Valentin Gagarin Committed by GitHub
Browse files

doc: actually document `lib.customisation.makeScope` (#294194)

* doc: actually document `lib.customisation.makeScope`
parent 32146f31
Loading
Loading
Loading
Loading
+122 −11
Original line number Diff line number Diff line
@@ -306,18 +306,129 @@ rec {
    in if drv == null then null else
      deepSeq drv' drv';

  /* Make a set of packages with a common scope. All packages called
     with the provided `callPackage` will be evaluated with the same
     arguments. Any package in the set may depend on any other. The
     `overrideScope'` function allows subsequent modification of the package
     set in a consistent way, i.e. all packages in the set will be
     called with the overridden packages. The package sets may be
     hierarchical: the packages in the set are called with the scope
     provided by `newScope` and the set provides a `newScope` attribute
     which can form the parent scope for later package sets.
  /**
    Make an attribute set (a "scope") from functions that take arguments from that same attribute set.
    See [](#ex-makeScope) for how to use it.

     Type:
       makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> AttrSet
    # Inputs

    1. `newScope` (`AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a`)

       A function that takes an attribute set `attrs` and returns what ends up as `callPackage` in the output.

       Typical values are `callPackageWith` or the output attribute `newScope`.

    2. `f` (`AttrSet -> AttrSet`)

       A function that takes an attribute set as returned by `makeScope newScope f` (a "scope") and returns any attribute set.

       This function is used to compute the fixpoint of the resulting scope using `callPackage`.
       Its argument is the lazily evaluated reference to the value of that fixpoint, and is typically called `self` or `final`.

       See [](#ex-makeScope) for how to use it.
       See [](#sec-functions-library-fixedPoints) for details on fixpoint computation.

    # Output

    `makeScope` returns an attribute set of a form called `scope`, which also contains the final attributes produced by `f`:

    ```
    scope :: {
      callPackage :: ((AttrSet -> a) | Path) -> AttrSet -> a
      newScope = AttrSet -> scope
      overrideScope = (scope -> scope -> AttrSet) -> scope
      packages :: AttrSet -> AttrSet
    }
    ```

    - `callPackage` (`((AttrSet -> a) | Path) -> AttrSet -> a`)

      A function that

      1. Takes a function `p`, or a path to a Nix file that contains a function `p`, which takes an attribute set and returns value of arbitrary type `a`,
      2. Takes an attribute set `args` with explicit attributes to pass to `p`,
      3. Calls `f` with attributes from the original attribute set `attrs` passed to `newScope` updated with `args, i.e. `attrs // args`, if they match the attributes in the argument of `p`.

      All such functions `p` will be called with the same value for `attrs`.

      See [](#ex-makeScope-callPackage) for how to use it.

    - `newScope` (`AttrSet -> scope`)

      Takes an attribute set `attrs` and returns a scope that extends the original scope.

    - `overrideScope` (`(scope -> scope -> AttrSet) -> scope`)

      Takes a function `g` of the form `final: prev: { # attributes }` to act as an overlay on `f`, and returns a new scope with values determined by `extends g f`.
      See [](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.fixedPoints.extends) for details.

      This allows subsequent modification of the final attribute set in a consistent way, i.e. all functions `p` invoked with `callPackage` will be called with the modified values.

    - `packages` (`AttrSet -> AttrSet`)

      The value of the argument `f` to `makeScope`.

    - final attributes

      The final values returned by `f`.

    # Examples

    :::{#ex-makeScope .example}
    # Create an interdependent package set on top of `pkgs`

    The functions in `foo.nix` and `bar.nix` can depend on each other, in the sense that `foo.nix` can contain a function that expects `bar` as an attribute in its argument.

    ```nix
    let
      pkgs = import <nixpkgs> { };
    in
    pkgs.lib.makeScope pkgs.newScope (self: {
      foo = self.callPackage ./foo.nix { };
      bar = self.callPackage ./bar.nix { };
    })
    ```

    evaluates to

    ```nix
    {
      callPackage = «lambda»;
      newScope = «lambda»;
      overrideScope = «lambda»;
      packages = «lambda»;
      foo = «derivation»;
      bar = «derivation»;
    }
    ```
    :::

    :::{#ex-makeScope-callPackage .example}
    # Using `callPackage` from a scope

    ```nix
    let
      pkgs = import <nixpkgs> { };
      inherit (pkgs) lib;
      scope = lib.makeScope lib.callPackageWith (self: { a = 1; b = 2; });
      three = scope.callPackage ({ a, b }: a + b) { };
      four = scope.callPackage ({ a, b }: a + b) { a = 2; };
    in
    [ three four ]
    ```

    evaluates to

    ```nix
    [ 3 4 ]
    ```
    :::

    # Type

    ```
    makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> scope
    ```
  */
  makeScope = newScope: f:
    let self = f self // {