问题描述
试图理解 callPackage
,所以查找了 its implementation 使用 lib.functionArgs
(source) 的地方,但是已经有一个 builtins.functionArgs
primop,一个别名__functionArgs
(implemented in C)。
lib.functionArgs
定义为
/* Extract the expected function arguments from a function.
This works both with nix-native { a,b ? foo,... }: style
functions and functions with args set with 'setFunctionArgs'. It
has the same return type and semantics as builtins.functionArgs.
setFunctionArgs : (a → b) → Map String Bool.
*/
functionArgs = f: f.__functionArgs or (builtins.functionArgs f);
上面的 __functionArgs
属性来自 setFunctionArgs
(source):
/* Add Metadata about expected function arguments to a function.
The Metadata should match the format given by
builtins.functionArgs,i.e. a set from expected argument to a bool
representing whether that argument has a default or not.
setFunctionArgs : (a → b) → Map String Bool → (a → b)
This function is necessary because you can't dynamically create a
function of the { a,... }: format,but some facilities
like callPackage expect to be able to query expected arguments.
*/
setFunctionArgs = f: args:
{
__functor = self: f;
__functionArgs = args;
};
我理解 setFunctionArgs
的作用,其声明上方的注释说明了为什么它是必要的,但我无法理解;该句子的两个子句都很清楚,但不确定第一个语句如何阻止实现第二个语句(没有 setFunctionArgs
,即)。
danbst also tried to elucidate this further,
lib.nix
添加 __functionArgs
attr 来模仿 __functionArgs
内置。它
用于将实际 __functionArgs
结果“传递”给消费者,因为
内置 __functionArgs
仅适用于最顶层的函数参数
但不确定“消费者”是什么,并且无法解压最后一个子句(即“builtin __functionArgs
仅适用于最顶层的函数 args”)。这是对 Nix 函数被柯里化这一事实的引用,以及
nix-repl> g = a: { b,c }: "lofa"
nix-repl> builtins.functionArgs g
{ }
?
lib.functionArgs
也没有解决这个问题,但我现在可能跑偏了。
给自己的笔记
__functor
记录在 Nix 手册的 Sets 下。
$ nix repl '<nixpkgs>'
Welcome to Nix version 2.3.6. Type :? for help.
Loading '<nixpkgs>'...
Added 11530 variables.
nix-repl> f = { a ? 7,b }: a + b
nix-repl> set_f = lib.setFunctionArgs f { b = 9; }
nix-repl> set_f
{ __functionArgs = { ... }; __functor = «lambda @ /nix/store/16blhmppp9k6apz41gjlgr0arp88awyb-nixos-20.03.3258.86fa45b0ff1/nixos/lib/trivial.nix:318:19»; }
nix-repl> set_f.__functionArgs
{ b = 9; }
nix-repl> set_f set_f.__functionArgs
16
nix-repl> set_f { a = 27; b = 9; }
36
解决方法
lib.functionArgs
包装 builtins.functionArgs
以提供对泛型函数的反射访问。
这支持使用 builtins.functionArgs
进行反射:
f = { a,b,c }: #...
现在考虑相同函数的eta abstraction:
f' = attrs: f attrs
这不支持使用 builtins.functionArgs
进行反射。使用 setFunctionArgs
,您可以恢复该信息,只要您还使用 lib.functionArgs
。
我建议避免反射,因为我见过的所有用它实现的东西都可以不用它来实现。它扩展了函数的定义,以包括通常应视为实现细节的内容。
无论如何,主要动机似乎是 callPackage
,如果您像在 ...
中那样更改所有包以添加 { lib,stdenv,... }:
,则可以通过正常的 attrset 操作实现。我确实对函数反射这个错误特性有病态的兴趣,所以如果有人发现另一个用例,请发表评论。