In some advanced nix code (haskell.nix) I spotted a use of __readFile
. What is the difference between that and the (documented) builtins.readFile
?
Looking at this nix code, it seems that some builtins are registered in the global environment with __
in the name, but this is stripped before adding them to the builtins
set, so there is no difference:
Value * EvalState::addPrimOp(PrimOp && primOp)
{
/* Hack to make constants lazy: turn them into a application of
the primop to a dummy value. */
if (primOp.arity == 0) {
primOp.arity = 1;
auto vPrimOp = allocValue();
vPrimOp->type = tPrimOp;
vPrimOp->primOp = new PrimOp(std::move(primOp));
Value v;
mkApp(v, *vPrimOp, *vPrimOp);
return addConstant(primOp.name, v);
}
Symbol envName = primOp.name;
if (hasPrefix(primOp.name, "__"))
primOp.name = symbols.create(std::string(primOp.name, 2));
Value * v = allocValue();
v->type = tPrimOp;
v->primOp = new PrimOp(std::move(primOp));
staticBaseEnv.vars[envName] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
return v;
}
That is exaclty the answer. To be complete, builtins (aka. primops) are either considered toplevel and directly available in the global environment, or hidden away with these two underscores. But all builtins are available unprefixed under the builtins
variable.
That being said, the prefixed version is seldom used. It may be worth to switch to the builtins.xxx
version or import all of them with with builtins;
.
And there are more fun facts. builtins.builtins
is the only builtin that has no __builtins
toplevel counterpart. And builtins.builtins
is builtins
itself, tying the knot and making builtins.builtins.builtins.readFile
a valid builtin. Nix has some quirky loopholes .
It is also interesting that makes some builtins so special, that they are available at top-level without requiring __
or builtins.
prefix
It is understandable for true
, false
, abort
, derivation
, placeholder
and import
(although not fully - being builtins and not keywords makes they overridable, one can write let true=false; in ...
but no let 1=2; in
), the same exception made for
-
isNull
(but not forbuiltins.isInt
) baseNameOf
dirOf
-
removeAttrs
(but not forbuiltins.hasAttr
) -
map
(but not forbuiltins.filter
) toString
-
fetchTarball
(but not forbuiltins.fetchurl
)
looks mystical.
UPD: it looks like the top-level list is different in HNix and C++ Nix: HNix extends it with mapAttrs
and trace