[Nix-dev] Questions about the all-packages fixpoint

Nicolas Pierron nicolas.b.pierron at nbp.name
Fri Mar 17 21:04:16 CET 2017


Hi Benno,

Before answering, let me give you a brief intro to the fix-extend
function combo.

The extend function is similar to the update operator of Nix `//`, the
main difference is that it give its left-hand side (`super`) as
argument to its right hand side.
But this extend function give an extra argument (`self`) to all its
arguments. When chaining these extend function, the `self` argument is
the same for the all layers.
When a fix point is applied to this chain of extend function, `self`
corresponds to the last set produced by the extend function chain.

Note that using `self` can easily cause infinite recursion, for
example if the addition of an attribute to the set depends on the
presence of an attribute in `self`.

On Fri, Mar 17, 2017 at 5:04 PM, Benno Fünfstück
<benno.fuenfstueck at gmail.com> wrote:
> 2. In `stage.nix`, why do we construct a *local* fixpoint just for
> all-packages? The current code is:
>
>     allPackages = self: super:
>       let res = import ./all-packages.nix
>         { inherit lib nixpkgsFun noSysDirs config; }
>         res self;
>       in res;
>
> Even more confusingly, if we look at `all-packages.nix`, it is a function
> defined like this:
>
>     { lib, nixpkgsFun, noSysDirs, config}:
>     self: pkgs:
>
> So the variable that is called `self` in `stage.nix` is bound to what is
> called `pkgs` in `all-packages.nix`, and `self` in `all-packages.nix`
> actually refers to what is called `res` in `stage.nix`!!!!

This is because this code is still legacy before the introduction of
the extend function.
We should not use this `self` argument under all-packages.nix, but
last time I tried to avoid changing any hashes.

Another reason why I did not address this, such as renaming it because
`self` is quite an overloaded name, and a naive search and replace
would not work as expected.
Note, there is also a package named `self`, for interpreting the Self language.

So `pkgs` of all-packages.nix is literaly the result of the fix-point,
given to all overlays under the name `self`.

> 1. An override is given the arguments `self` and `super`, as expected. But
> why does `super.callPackage` resolve dependencies from `self`?

`callPackage` take a function as argument, or a path to a function,
and list the arguments of it.  It then create a set with the requested
argument and a set given as argument.
In `top-level/splice.nix`, the callPackage function is given an
argument which is constructed from the result of the fix-point.
Thus `super.callPackage` will fill the arguments of the functions with
the value taken within the result of the fix-point.

> I would have
> expected it to not know about `self`, and resolve dependencies in the scope
> of `super`. If I wanted this behaviour, I would have used
> `self.callPackage`, but the current behaviour makes that unnecessary. Why is
> this implemented like that?

The history of all-packages.nix used to be that we had a "rec" keyword
on top of the attribute set.
Then we added the `self` reference that you complained about in (2) in
order to be able to override packages.

If `super.callPackage` were taking all its arguments from `super`,
then this would imply that you could not easily override the argument
of packages which are inside the dependencies of another one.
By taking the packages from `self`, we can easily replace it for all its uses.

> This is confusing to me. Also, why do we need `self` and `pkgs`? Wouldn't
> one of them be enough?

Yes, but again, renaming these variables is hard without the proper
tooling to guarantee that we are not making any mistake.


-- 
Nicolas Pierron
http://www.linkedin.com/in/nicolasbpierron - http://nbp.name/


More information about the nix-dev mailing list