There are a couple of pitfalls:
- It’s possible to introduce a hard to debug error
infinite recursion
when shadowing a variable, the simplest example beingrec { b = b; }
.- combining with overriding logic such as
overrideAttrs
function in nixpkgs has a suprising behavour of not overriding every reference.
While I agree that rec
attrsets suffer from the issues mentioned, I would not migrate back¹ to let
expression, at least for things like version
.
I would argue that it is quite unlikely that simple attribute like version
would cause an infinite recursion – it would be a literal value most of the time. So of the two mentioned problems, only the second one would be relevant here. And while let
expression makes it explicit that the value is not overridable when you see the expression, I would expect most people to encounter the issue when calling pkg.overrideAttrs (attrs: { version = "foo"; })
without inspecting the package – that would not work with let
either.
Therefore, let
expression does not solve either of the aforementioned pitfalls for your example.
There is a pattern that allows using overrideAttrs
:
{ stdenv, fetchurl }:
let self = stdenv.mkDerivation {
pname = "foo";
version = "1.0";
src = fetchurl {
url = "https://foo-${self.version}.tar.gz";
sha256 = "...";
};
}; in self
But I am not sure the decreased legibility is worth it. Also it does not currently work with python.pkgs.buildPythonPackage
.
Of course, for complex values, narrowly scoped let
expressions should be preferred but I would avoid calling rec
an antipattern.
¹ We used to define version
using let
expression quite a lot, but when mkDerivation
started accepting pname
and version
, instead of just name
, we started passing them to the derivation directly to reduce verbosity.