-
-
Notifications
You must be signed in to change notification settings - Fork 14.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce types.orderOf
#97392
base: master
Are you sure you want to change the base?
Introduce types.orderOf
#97392
Conversation
lib/types.nix
Outdated
The contents of the DAG entry. | ||
''; | ||
}; | ||
after = mkOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could take the opportunity to change the names of after
and before
? E.g., wantedBy
and wants
to match the naming in systemd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Systemd also has after
and before
actually (see man systemd.unit
), and those make more sense for a DAG than wantedBy
and wants
.
Shouldn't this be just a library function? It's a useful algorithm, but I doubt that it should be a type like this. It forces the attribute names like |
I don't know why, but every time I hear about acrylic graphs I think of like nail extensions 🤣 |
@roberth I guess I can make this a library function in addition as well, though the algorithm is really just a simple wrapper around I think it makes sense for it to be a type, because that's the most convenient way to use it. It takes care of a bunch of boilerplate:
|
@infinisil Let's see if we can improve this then. My concern is that a small convenience for the module author comes at the cost of usability for, well, users. Perhaps a usage by the module author could look like this:
Systemd works like this:
This seems to be the inverse of what this PR is doing (unless I'm not mapping between the domains correctly??). To quote this PR's
Am I reading this right? All I know for sure is that the user should get to read docs like the first of the two. |
@roberth Oops I think I wrote that doc description the wrong way around. I'll give your idea some thought though, I think I like it! |
Changed this a bunch, now it's a lot simpler, and only requires giving a |
I’m having doubt whether allowing arbitrary turing complete predicates (aka |
In particular, I don’t know if you can actually have any guarantees about the result being acyclic by modeling it this way? Also properties I would expect from a DAG, like “if B is a subtree and A is before B, then every child of B is after A” are not a given. |
Nix is a lazy Turing complete language, which means that behind everything that "looks like data" is a thunk that may evaluate to bottoms like exceptions or the infinite recursion you seem to want to prevent somehow.
The type converts the definitions to a list that is sorted according to the |
<replaceable>elemType</replaceable> } | ||
</term> | ||
<listitem> | ||
<para> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that types can produce a result that is of a very different shape is non-obvious but essential to the understanding of orderOf
, so it seems like a good idea to briefly explain this aspect of orderOf
first.
<term> | ||
<varname>types.orderOf</varname> { | ||
<replaceable>before</replaceable> ? a: b: false, | ||
<replaceable>elemType</replaceable> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<replaceable>elemType</replaceable> } | |
<replaceable>wrappedType</replaceable> } |
elemType
would have been the entire attrsOf foo
for example, despite the name hinting that it'd only be elemType = foo
.
Yes, but it would mean an “infinite recursion” in practice, right? Which is not very user-friendly. Plus, this is still an issue in my mind:
|
It would be nice to have a
That's what this type is all about, to ensure that either it will be the case or an error is raised. Perhaps you could formulate your concern differently or provide an example that is handled poorly by this type and tell us what you would expect it to do instead. |
let | ||
nodeAttributes = (attrsOf elemType).merge loc defs; | ||
entries = mapAttrsToList nameValuePair nodeAttributes; | ||
sortedEntries = toposort before entries; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toposort
is O(n^2)
, so I’m kinda afraid this might slow down the module system even more if it’s used in a few places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if it's possible to implement toposort
more efficiently. However I can look into whether it's possible to implement a different interface more efficiently, e.g. something that isn't based on a before
function but on a successor mapping instead, e.g. graphToposort { foo = [ "bar" ]; bar = []; }
representing foo
having a directed edge to bar
. In theory, topo sorts can be O(|N| + |E|)
, though that might not be implementable in Nix.
Which is what I’m not sure about. I looked at the implementation of |
Maybe more importantly: Do we have applications for this type? |
Ignoring backwards compatibility, this could be used for:
|
After some discussion on IRC, here is my take on this. I think we need an abstraction over this, because, as @infinisil stated, it could be used in few places in nixpkgs, instead of having, shall I call them hacks?, stuff like This abstraction can be, in my opinion, anything, as long as it is just generic enough for wide usage, but not too generic so modules don't have to re-implement too much (untested) logic in their definition. Which brings me to my next point, testing. Having an abstraction also means that it can be thoroughly tested and vetted. If each module that needs this starts to re-implement its own logic for this, we will probably end up with some of them not getting it quite right and edge cases where the resulting configuration will not be the expected result. Onto an example, I am using this for #105319. The relevant places to look are:
I wouldn't have wanted to re-implement the logic proposed here. As it turns out, it was available as a type, but it would have worked just as well with a function to call inside the |
@rissson Did you plan to migrate the pam modules to a partial ordering? As things stand, you don't need partial ordering functionality and the much simpler and more efficient Doesn't mean that |
No, indeed.
Might be an idea. |
I marked this as stale due to inactivity. → More info |
Motivation for this change
This introduces a new type
orderOf
for representing partially ordered sets (or equivalently, directed acyclic graphs)This is similar in functionality as the dag type in home-manager.
Ping @rycee @roberth
Things done