Is it possible for us to remove builtins.functionArgs?

The drawback of builtins.functionArgs is that when we write {lib, pkgs, ...}@args: { x = args.x}, if the function is called by callPackage or a similar mechanism that uses builtins.functionArgs, we actually cannot retrieve the x field as expected.

The difficulty in removing builtins.functionArgs lies in the fact that this built-in function is heavy used by callPackage.

A possible solution is to add ... at the end of the parameter list of the code for all packages, like {lib, pkgs, ...}: {}, to avoid relying on builtins.functionArgs. I’m not sure if this is feasible or what potential issues might arise, so I’m initiating this discussion.

2 Likes

Also relevant, this deferred RFC: [RFC 0058] Named Ellipses by deliciouslytyped · Pull Request #58 · NixOS/rfcs · GitHub

1 Like

I’d rather like to question the need for individual packages as function parameters; obviating callPackage entirely. Why couldn’t we just pass all pkgs?

The only thing it really affords us is to override the meaning of package names but the same could be achieved using pkgs // { foo = ...; } or overlays/extensions.

1 Like

Short answer: no. Removing a non-experimental builtin supported since nixpkgs/lib/minver.nix (2.3) breaks expressions. Makes talk of reproducibility kind of moot.

Long answer: functionArgs is an ugly capability that turns nice and simple laws into mere rules of thumb. One rule comes to mind, but there’s probably a lot more: eta expansion isn’t a guaranteed correct refactor: functionArgs (x: f x) is {} and often not equal to functionArgs f.
So while we can’t remove it, we can choose not to use it.

I’ve played with that in Single package fixpoint by roberth · Pull Request #296769 · NixOS/nixpkgs · GitHub, but I ended up using it anyway, because otherwise more boilerplate would be needed. I think the main thing that PR demonstrated wrt functionArgs is that we can have an incremental migration path to a different solution/solutions that solve the flaws of callPackage.

In package.nix files, you’d pick a new solutions like this:

-({ stdenv, fetchurl, zlib }:
+{ mkPackageWithDeps }: mkPackageWithDeps ({ stdenv, fetchurl, zlib }:

I believe one reason is cross compilation splicing, although that seems solvable.
The other one I believe is that doing overrides with pkgs // { ... } will produce many large attrsets, which is quite inefficient.
An alternate representation like feat: persistent lists by ConnorBaker · Pull Request #11767 · NixOS/nix · GitHub or perhaps [WIP] Lazy attribute names by infinisil · Pull Request #4154 · NixOS/nix · GitHub might improve that.

1 Like

If we’re redesigning callPackage/mkDerivation, we don’t need splicing and shouldn’t propagate it. I link to simpler, saner cross-compilation · Issue #227327 · NixOS/nixpkgs · GitHub all the time these days, but it really is the ideal we should be approaching, and would certainly not require any builtins.functionArgs.

5 Likes

Regardless of the simpler cross design @emily linked which I also support, you can also just make pkgs itself spliced and it should work the same. See splice.nix: make `pkgs` `splicedPackages` by Artturin · Pull Request #349316 · NixOS/nixpkgs · GitHub

3 Likes