7

To call a Nix function that uses set destructuring, you need to pass it a set with exactly the keys it requires, no more and no less:

nix-repl> ({ a }: a) { a = 4; b = 5; }
error: anonymous function at (string):1:2 called with unexpected argument ‘b’, at (string):1:1

The exception to this is if the function's argument list contains an ellipsis at the end:

nix-repl> ({ a, ... }: a) { a = 4; b = 5; }
4

However, most of the packages in nixpkgs consist of a default.nix file containing a function which is not defined with this ellipsis. Yet, somehow when you use callPackage, it manages to call these functions and pass them only the arguments that they need. How is this implemented?

toraritte
  • 4,130
  • 3
  • 26
  • 43
Chris Martin
  • 28,558
  • 6
  • 66
  • 126
  • For anyone wondering where `callPackage` is implemented (and how to find lambda definitions), see this answer: https://stackoverflow.com/a/56124590/1498178 – toraritte May 14 '19 at 15:36

1 Answers1

6

There is a reflection primop, that can deconstruct function argument:

nix-repl> __functionArgs ( { x ? 1, y }: x )
{ x = true; y = false; }

callPackage then iterates over those attribute names, fetches required packages and constucts the attrset of packages, that is fed later to called function.

Here's a simple example:

nix-repl> callWithExtraArgs = f: args:
            let
              args' = __intersectAttrs (__functionArgs f) args;
            in
              f args'

nix-repl> callWithExtraArgs ({ x }: x + 1) { x = 4; y = 7; }
5

To browse Nix primops, go to 15.5. Built-in Functions in the Nix manual (or see the docs of the unstable branch).

toraritte
  • 4,130
  • 3
  • 26
  • 43
danbst
  • 2,615
  • 13
  • 34
  • Initially I added the line "_`__functionArgs` is the same as `builtins.functionArgs`_" to the edit above because both evaluate to `«primop»` in `nix repl`, but [the comment and code in `trivial.nix`](https://github.com/NixOS/nixpkgs/blob/8aca4a8fa7b053e67c7200f426826cd93afd92a8/lib/trivial.nix#L310) made me unsure about that statement. (The link shows `setFunctionArgs` as well because can't wrap my head around its seemingly circular definition..) Same goes to `__intersectAttrs` and `builtins.intersectAttrs`. – toraritte Feb 10 '21 at 17:31
  • 1
    @toraritte they are same, it is just lib.nix *adds* __functionArgs attr to mimic __functionArgs builtin. It used to "pass" actual __functionArgs result down to consumers, because builtin __functionArgs only works on top-most function args. Yeah, mind wrapping – danbst Feb 10 '21 at 19:34