Conversation
With removePrefix introduced in a future commit this law can then be
used to derive
removePrefix p (append p s) == subpath.normalise s
=> (wrap with append)
append p (removePrefix p (append p s)) == append p (subpath.normalise s)
=> (append is not influenced by subpath normalisation)
append p (removePrefix p (append p s)) == append p s
=> (substitute q = append p s)
append p (removePrefix p q) == q
Not included in the docs because it's not that important, just shows
that the first statement is more general than the second one (because
this derivation doesn't work the other way)
lib/path/default.nix
Outdated
|
|
||
| - Equivalent to whether some subpath exists that can be appended to the first path to get the second path: | ||
|
|
||
| hasPrefix p q <-> exists s . append p s == q |
There was a problem hiding this comment.
| hasPrefix p q <-> exists s . append p s == q | |
| hasPrefix p q | |
| = | |
| ∃s. (append p s == q) |
A good definition of ∃ is easy to find with a search engine nowadays, whereas exists is imaginary syntax that's impossible to find.
I think it's ok to use = in meta syntax.
I had to look up that <-> is iff. Maybe I'm dumb, because I did take math courses in uni... Anyway, the search result for <-> isn't as good as ∃, for what it's worth, but most of all, we don't need this operator.
There was a problem hiding this comment.
Or double equal sign like the preceding law in the diff
| hasPrefix p q <-> exists s . append p s == q | |
| hasPrefix p q | |
| == | |
| ∃s. (append p s == q) |
I think the implementation could perhaps be optimized for the true case, because then you don't need to find the other root, or allocate path component lists, but you'd have to measure it. Something for a later PR maybe.
So just the suggestion (either one) and then lgtm
There was a problem hiding this comment.
I think I'd rather remove the extra newlines to be consistent with the rest of this file, sounds good otherwise!
There was a problem hiding this comment.
I'm curious what people think, so unscientific twitter poll.
There was a problem hiding this comment.
We could also do this to avoid the
Equivalent to whether some subpath
sexists that can be appended to the first path to get the second path:hasPrefix p q == (append p s == q)
But also, the two =='s do look a bit weird, I think something like <-> would be better, but yeah it's not that well known, I don't mind either way
How about <=> or
There was a problem hiding this comment.
<=> is what I would expect.
There was a problem hiding this comment.
How about just having this, no additional formula:
hasPrefix p qis only true ifq == append p sfor some subpaths.
(latest commit uses this now)
There was a problem hiding this comment.
I think that's best, overall.
Though personally any of the notation above is fine too. I've finally stopped overriding \iff from <=> to <-> in LaTeX 😂
dd30838 to
78b24ab
Compare
lib/path/default.nix
Outdated
There was a problem hiding this comment.
| Whether the second path is a prefix of the first path, or equal to it. | |
| Throws an error if the paths don't share the same filesystem root. | |
| Whether the first path is a prefix of the second path. |
It was the wrong way around, and the filesystem root thing really doesn't need to be mentioned, since you can't even produce paths that would cause that error in current Nix.
(applied in latest commit)
78b24ab to
0042d58
Compare
lib/path/default.nix
Outdated
There was a problem hiding this comment.
Maybe also something like
| Whether the first path is a prefix of the second path. | |
| Whether the first path is a component-wise prefix of the second path. |
To make it clear that hasPrefix /foo/b /foo/bar == false.
0042d58 to
592213a
Compare
| assert assertMsg | ||
| (path1Deconstructed.root == path2Deconstructed.root) '' | ||
| lib.path.hasPrefix: Filesystem roots must be the same for both paths, but paths with different roots were given: | ||
| first argument: "${toString path1}" with root "${toString path1Deconstructed.root}" | ||
| second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"''; |
There was a problem hiding this comment.
This should not cause the function to fail, it should instead just return false (Edit: I don't think so anymore, see #237610 (comment))
There was a problem hiding this comment.
This is all a bit messed up, because the reason this check is here is because we decided to limit the number of assumptions the path library makes to a minimum, see the design document. Notably we don't assume that there's only one filesystem root, meaning that theoretically paths could have no common ancestor. The reason for this is to ensure compatibility with how NixOS/nix#6530 implements lazy paths, introducing a way to have multiple filesystem roots.
But it's kind of ridiculous, Nixpkgs shouldn't have to ensure forwards compatibility with a potential future Nix version. We can't even test this code here right now without going through hoops!
On the other hand, having this check here ensures that we won't have any buggy behavior in case the above PR gets merged like that, and it doesn't cause any problems if the PR isn't merged either, so it's harmless and just a safety measure.
So because of that I'd say: Let's continue without the assumption of singular filesystem roots and have this check here.
There was a problem hiding this comment.
Regarding throwing vs not throwing. I think it should definitely throw for now because:
- We can always remove throws afterwards, it's backwards compatible
- The notion of different filesystem roots is very unexplored, we aren't sure what the semantics should be like yet
- I can imagine however that in most cases, this would indicate an user error, and it would be better to notify the user instead of "failing" silently
- Code that needs to ensure it doesn't throw can still check whether the two paths have the same filesystem root beforehand
So in conclusion, no change is necessary here imo.
And I did test this code path at least locally:
❯ nix run github:edolstra/nix/lazy-trees -- repl -f lib
Welcome to Nix 2.17.0pre20230602_3c4d678. Type :? for help.
Loading installable ''...
Added 411 variables.
nix-repl> p = (builtins.fetchTree { type = "git"; url = ./.; }).outPath
nix-repl> path.hasPrefix p ./.
error: lib.path.hasPrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
first argument: "/home/tweagysil/antithesis/nixpkgs/" with root "/home/tweagysil/antithesis/nixpkgs/"
second argument: "/home/tweagysil/antithesis/nixpkgs" with root "/"
There was a problem hiding this comment.
- We can always remove throws afterwards, it's backwards compatible
We'd break code that relies on this function for input validation. Just because it works for valid inputs doesn't mean that changing it won't lead to hard to troubleshoot problems for others. Basically your third point, but with a time / change aspect added.
There was a problem hiding this comment.
True, though I'd argue that backwards compatibility conventionally only means "If it worked before it should work in the future", a one-way implication, so something not working now doesn't mean it will continue not working in the future.
There was a problem hiding this comment.
But it's kind of ridiculous, Nixpkgs shouldn't have to ensure forwards compatibility with a potential future Nix version. We can't even test this code here right now without going through hoops!
I kind of agree, but like you, I care too much about the future of lib.
The hoops are packaging that branch and using it in lib/release.nix. However, that would be irresponsible because of how broken it is. While I am confident that this particular behavior is good, users might adapt their programming to behaviors that are bad. Paths are already confusing to users, so we should discourage all adaptation to path related behaviors that will not be released.
There was a problem hiding this comment.
Users won't run into this error anyways right now though, the PR isn't merged yet, or might not even get merged with this behavior ever.
And if it were to get merged, I still think throwing an error here is better than returning false for the other reasons at least. Whether we want to change this behavior in the future and whether we should consider that backwards incompatible should be left for then, I don't think it makes sense to discuss this much here.
Do you agree that the code is fine as is?
Description of changes
Adds a function to
lib.path:lib.path.hasPrefix :: Path -> Path -> Bool: Whether the first path is a prefix of the second path:This is split off from #210423, relating to other work in the path library effort.
This work is sponsored by Antithesis ✨
Things done