Conversation
The original doc did not help with understanding at all, and the wikipedia link was actively harmful.
tjni
left a comment
There was a problem hiding this comment.
I like the fleshed out explanation building up to how this can work. Thank you.
I left some comments but they are just my opinions and need not be followed up on (or followed up on before merging this PR).
| Compute the fixed point of the given function `f`, which is usually an | ||
| attribute set that expects its final, non-recursive representation as an | ||
| argument: | ||
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. |
There was a problem hiding this comment.
Nit: I think it reads a little better like: "In other words, it returns the value x satisfying x = f x."
lib/fixed-points.nix
Outdated
| argument: | ||
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. | ||
|
|
||
| `f` is usually returns an attribute set that expects its final, non-recursive representation as an argument. |
There was a problem hiding this comment.
Nit: "is usually" => "usually"
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. | ||
|
|
||
| `f` is usually returns an attribute set that expects its final, non-recursive representation as an argument. | ||
| `f` must be a lazy function. |
There was a problem hiding this comment.
I personally feel this lazy explanation would be more powerful somewhere in the "How it works" section to give some insight into how it's possible to compute the fixed point.
lib/fixed-points.nix
Outdated
|
|
||
| Type: fix :: (a -> a) -> a | ||
| This example did not _need_ `fix`, and arguably it shouldn't be used in such an example. | ||
| However, `fix` is useful when your `f` is a parameter, or when it is constructed from higher order functions. |
There was a problem hiding this comment.
To me, this is an area that can be expanded. To this point, the only example in my mind is the rec attribute set, so f just seems like another way to represent that. It feels like I could pass such an attribute set as a parameter or perhaps construct one from higher order functions without having to pass through fix.
There was a problem hiding this comment.
We now refer to extends as a use case.
fricklerhandwerk
left a comment
There was a problem hiding this comment.
Somewhere in there is hidden a great introduction to the module system, but it's not visible yet.
lib/fixed-points.nix
Outdated
| ``` | ||
|
|
||
| Type: fix :: (a -> a) -> a | ||
| This example did not _need_ `fix`, and arguably it shouldn't be used in such an example. |
There was a problem hiding this comment.
Why did we choose this example then if it's not helpful to understand practical applications of fix?
lib/fixed-points.nix
Outdated
|
|
||
| Type: fix :: (a -> a) -> a | ||
| This example did not _need_ `fix`, and arguably it shouldn't be used in such an example. | ||
| However, `fix` is useful when your `f` is a parameter, or when it is constructed from higher order functions. |
There was a problem hiding this comment.
I can only guess at what this means. Is it when f is a parameter in a scope where we want to evaluate a recursive attrset and therefore we want to abstract over that? (And still why would we do it that way instead of defining a rec set?) What would f constructed from higher order functions look like? Please make an example for each.
| ```nix | ||
| fix = f: | ||
| let self = f self; in self; | ||
| ``` |
There was a problem hiding this comment.
What helped me understand this is an eval trace where we apply x to f and see how self is replaced by x, yielding the let expression in the previous sample.
lib/fixed-points.nix
Outdated
|
|
||
| Nix evaluates this recursion until all references to `self` have been | ||
| resolved. At that point, the final result is returned and `f x = x` holds: | ||
| `let` bindings are nice, but as it is with `let` bindings in general, we may get more reuse out of the code by defining a function. |
There was a problem hiding this comment.
This is a very handwavy motivation. It does not explain at all why we would go down that path.
| Compute the fixed point of the given function `f`, which is usually an | ||
| attribute set that expects its final, non-recursive representation as an | ||
| argument: | ||
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. |
There was a problem hiding this comment.
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. | |
| `fix f` computes the fixed point of the given function `f`. | |
| This is the value that satisfies `f x == x`. |
"In other words" is the function definition, we already have that and it doesn't help.
What's missing here though is a non-magical explanation of how that value is found and when it works at all and why.
For instance, you can't say fix (x: 1.0 / x) although clearly the answer should be 1.
You also can't say fix ({a,b,...}: {a=1;b=2;c=a+b;}) for reasons unclear to me.
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. | ||
|
|
||
| `f` is usually returns an attribute set that expects its final, non-recursive representation as an argument. | ||
| `f` must be a lazy function. |
There was a problem hiding this comment.
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. | |
| `f` is usually returns an attribute set that expects its final, non-recursive representation as an argument. | |
| `f` must be a lazy function. | |
| `fix f` computes `x` such that `x = f x`, also known as the fixed point of `f`. | |
| In other words, it calls `f` with the result of itself. | |
| This can only be meaningful when `x` is a value that can be partially evaluated, such as an attribute set, a list, or a function. | |
| This way, `f` can use one part of `x` to compute another part. |
I'd suggest a wording something like this.
An example with a list and a function would also be good.
There was a problem hiding this comment.
This is great!
| `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. | |
| `f` is usually returns an attribute set that expects its final, non-recursive representation as an argument. | |
| `f` must be a lazy function. | |
| `fix f` computes `x` such that `x == f x`, also known as the fixed point of `f`. | |
| This can only be meaningful when `x` is a value that can be partially evaluated, such as an attribute set, a list, or a function. | |
| This way, `f` can use one part of `x` to compute another part. |
I'd remove the "in other words" because it misleads the reader into tying brain knots.
There was a problem hiding this comment.
I'd rather you don't edit my own comment fwiw! (why does GitHub even allow that though..) I edited it back now.
In particular, the "This can only be meaningful when" part is intentional, it should not be "This is only meaningful when". Because just using e.g. an attribute set in the result doesn't mean you'll get something "meaningful", e.g. f (attrs: { x = attrs.x + 1; }).
The only thing we know here is that if you don't use an attribute set, a list or a function, it's definitely going to either result in an infinite recursion (f (x: x + 1)), or in a constant value (f (x: 1)), which is definitely not meaningful.
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2023-07-13-documentation-team-meeting-notes-63/30450/1 |
|
See also my related proposed rewrite of the |
Done together in and after the docs team meeting Co-Authored-By: Robert Hensing <robert@roberthensing.nl>
infinisil
left a comment
There was a problem hiding this comment.
Worked on this together in the docs team meeting today, approved!
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2023-10-12-documentation-team-meeting-notes-86/34118/1 |
The original doc did not help with understanding much, and the wikipedia link was actively harmful.
Description of changes
Starts with succinct reference docs for what is TECHNICALLY a simple function, then goes into explanation.
I wish I could stress "technically" even more. Basically all of its complexity is emergent, as it mirrors a math concept that most programmers are not familiar with, in a language paradigm many aren't familiar with either.
However, it is self-contained and that makes this place a natural location for the explanation.
Things done
sandbox = trueset innix.conf? (See Nix manual)nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage./result/bin/)