Please, explain like I am 5 years old how that finalAttrs: {}
design pattern works in tandem with stdenv.mkDerivation
.
And, possibly, how can it be adapted to work with other frameworks (like, say, buildGoModule
).
I was playing with nix repl:
> fun = finalAttrs: { a=1; b = finalAttrs.a+1; c = finalAttrs.b+2; }
> fun { a=0; }
nix-repl> fun ( { a = 0; })
{ a = 1; b = 1; c = error: attribute 'b' missing
at «string»:1:45:
1| finalAttrs: { a=1; b = finalAttrs.a+1; c = finalAttrs.b+2; }
| ^
Did you mean a?
nix-repl> fun (fun { a = 0; })
{ a = 1; b = 2; c = 3; }
nix-repl>
However it didn’t help me too much.
2 Likes
It’s more like this:
> fun = finalAttrs: { a=1; b = finalAttrs.a+1; c = finalAttrs.b+2; }
> fix = f: let x = f x; in x
> fix fun
{ a = 1; b = 2; c = 4; }
The magic is that fix
function. It calls a function with its own result as the argument (that’s what let x = f x
does). This is possible because of laziness. So fun
returns { a = 1;, b = <thunk>; c = <thunk>; }
. If you evaluate b
, then it’ll evaluate finalAttrs.a + 1
, which means reaching into the attrset that was already returned and pulling out a
, which is already 1
.
That version of fix
is efficient, but it’s functionally equivalent to this:
fix = f: f (fix f)
So maybe you can see how this relates to your observation about fun (fun { a = 0; })
, in that it takes it to the extreme and evaluates to infinitely re-apply fun
to itself in that manner.
5 Likes