-
Notifications
You must be signed in to change notification settings - Fork 12.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
Document "latent requirements" of PartialOrd #50230
Comments
In the RFC thread for Irreflexivity is implied by said notational axiom and the semantics of Thus, instead of adding the axiom of Irreflexivity to the docs, one could also add the notational axiom (which we probably want to add anyway). The docs are also unclear or plain wrong in regards to the provided methods
This implies that one has to manually implement |
@RalfJung mentioned on IRC:
That is, the docs should state that |
Agreed. This is stated explicitly for PartialEq, it seems it as forgotten here.
I think that's the wrong way to think about it: PartialOrd is only a single operation, |
Yeah that sounds pretty reasonable. I'd express this as "> is the dual to <", I think that's the common terminology in maths. I briefly wondered how this relates to antisymmetry... but antisymmetry is a strange axiom in this context. In fact, antisymmetry is impossible to violate! If (EDIT: This assume |
Wouldn't we then need to require that if EDIT: I see this as just trading one axiom for another. We probably should document the axioms both in term of the comparisons |
That's the "notational" (or "duality") axiom: |
Aha! Yeah if we add that axiom then we don't need anti-symmetry. Nice! The wikipedia page on strict partial orders actually mentions this, that one axiom follows from the other two:
(just note that we don't require irreflexivity yet) |
So to make a more concrete proposal. We currently have
Including all feedback above, we could change that to (strawman):
I am not sure if everything does indeed follow properly from |
@gnzlbg Awesome, thank you! A few things though: (1.)
Are you sure? I don't think transitivity (" (2.)
I would change this to "Strict partial ordering relationS." as the trait adds (3.)Maybe we want to explicitly state that (4.)
I don't think you only need duality and transitivity of
|
I was talking here about
So when I wrote the axioms in term of
Yes you are correct, I ninja edited the post.
Step by step: "if
The only requirement of |
Mhh, yes that's tricky :/ But as far as I understand it, In fact, I think that Maybe the first paragraph of
I'd say so.
I'm not sure about your first step: you say "
So we still have two possibilities: either
This only requires everything to be comparable with itself, which doesn't help us because But again, maybe I'm missing something. |
@gnzlbg nice first strawman! I overall like it. I think before stating any axioms, however, we should define
As @LukasKalbertodt pointed out, these statements are not well-typed:
I'd add "and from the definition of
This is some kind of congruence statement: Essentially, we demand that So maybe, instead of what I said above, the real meaning of "
However, these may be too strong for irreflexive equalities? Can we derive one of these axioms from the other? Or find one nicer axiom that implies both of them? |
So, I've only been half-following this. I'm afraid I'll really only just be adding to the noise; but here's how I feel inclined to look at it:
|
Tagging with @rust-lang/lang; they need to make a call before we can write docs. |
Seems reasonable. The docs in this comment seem plausible to me. Would it make sense to use rfcbot to gather consensus asynchronously? |
Is there a recommended way to put up a documment that we could quickly edit for this collaboratively? It appears to me that after @LukasKalbertodt and @RalfJung comments we are 95 % there, we just need to finish it, and for that it might be better if we would just have something that's "uptodate" and that we can quickly eidt. |
@gnzlbg I don't know if that's what you had in mind, but I created a HackMD document from your comment. Everyone can login via GitHub and edit. But it might be better to have a tiny repository where we submit the documentation as PR and we can leave line comments? |
@LukasKalbertodt thanks! I worked a bit on it there and this is how it looks now (it would be cool if the tool could create diffs, so maybe a github repo would be better for something like this in the future):
|
Yes, diffs and PR-comments would be nice. I created a temporary repository and submitted the current documentation as PR. I'd suggest we could discuss everything there. I already left a few comments on the PR. I also gave you push access to the repo, so feel free to change things as you like. |
(small note: |
Looks like C++ went with something a bit more complex. |
@eddyb The latest paper is P0515r3. The most relevant section for this discussion is probably "Section 2: Design" which defines the following comparison categories as types:
The values of the equality categories are:
This is very similar to rust: interpret The orderings are also pretty similar:
The paper is a bit light on math details; maybe some of this is covered in Stepanov's "From Mathematics to Generic Programming" or "Elements of Programming, worth checking this out. Basically, the paper says that a type should implement The most interesting examples are:
|
Another related concern I have with how the current traits work is that there is no way to express that some impl PartialOrd<Foo> for Bar { ... } there is no way to implement |
Cc #81198 |
I really like the proposal above by @gnzlbg, but we are still abusing terminology from math. Partial and full equivalence relations as well as partial and total orders are all defined using binary relations on the same set—which is roughly analogous to types. Since the traits If we use @RalfJung's encoding of a binary relation (i.e., a function, No clue how one would adapt reflexivity and irreflexivity though. I feel like one can come up with some form of argument that invokes vacuous truths. Something like the function |
Indeed symmetry can be defined on "heterogeneous" relations like you say, but (ir)reflexivity just doesn't make sense in general. |
@RalfJung, should |
I am not sure what would be the point of such a requirement. While |
I realize that, but Rust is intentionally not the most "theoretically sound" language (nor should it be). Consequently there not existing some beautiful theoretical justification—as nice as that would be—is not enough reason to not add a requirement. After all, The dilemma I am facing is differentiating between the semantics of a type not implementing For example, it is "obvious" why This is why I suggested the requirement that the type is either uninhabited or have at least two terms (potentially being the same) that evaluate as equivalent. This "solves" this dilemma since there is now a clear distinction between not implementing the trait and implementing its most degenerative form. I cannot come up with a type where not implementing Edit After reflecting on what I said, there are two interpretations of why a type would not implement |
You say that, and then you go on making arguments based entirely on theoretic concerns about the "structure" that a trait imbues on a type. I really don't know how to make sense of that.^^ Usually in partial equivalence relations (but you probably know this), struct Invalid;
impl PartialEq for Invalid {
// just always return `false`
} So, I guess I don't share your issues with types whose equivalence relation is empty. Yes, there is a trivial partial equivalence relation for each type. However, note that there even is a trivial proper equivalence relation for each type, namely always returning FWIW there also is a trivial and fairly canonical (I would say) Can you come up with any concrete problems in real consumers of |
I suppose what I meant—but clearly did a poor job in conveying—is that I don't need the super technical encoding of some concept. While I realize having words like "structure" are more technical than others; from the perspective of math, it is very much informal (i.e., I am ignoring any formulation based on first-order logic/type theory of what that term means). I also hope I did not come off as rude or dismissive. Not that it matters one iota what I think, but I very much love Rust and have immense respect/envy for the language team. I hope my comments have not offended/upset you (or anyone else); but if so, I apologize. Any frustration I have is at myself for the difficulty I am having in gaining an intuition of what should likely be a pretty easy concept. I realize that As I said above, I realize that one can easily implement pretty much any trait they want for almost any type; but for some reason, my stupid brain has an intuition as to why one would not want to implement I think we have come to a point where I just need to reflect more because I understand what you mean from a technical perspective that having I cannot think of a concrete problem for "real consumers" of |
No offense was taken, I enjoyed this fun little exchange. :) But I agree that there's not much to go on with here, since sadly we don't have the tech for me to meet you in your brain space where you "feel" why things make sense on way but not the other. I know very well that expressing such intuition in words that make sense to other humans can be hard; I hope in pondering these issues you reach a better understanding that either resolves this concern or leaves you in a better way to express it. :) |
I often wish Rust used more Admittedly that's a bit of a tangent, but maybe it'll help give you another scenario on which to reflect, Zack. |
Wow, I had no idea that |
Yup, that's right. Same as how the only place where the order of the fields in a struct matters is in how it impacts the derive. |
All arguments aside, |
My brain decided to just not work for a little bit. Now that I've gotten more sleep, I'm embarrassed by my confusion above. I think there would be some use for a "true equality" trait. I feel like |
So you mean, like |
Either way, I think this particular issue can be closed. |
I think there is a lot of valuable information buried in this issue, like this comment. Sadly, the work of actually turning this into new docs for |
OK, perhaps we can modify the issue title. I'm a bit reluctant to "add new contracts" to |
Yeah, this issue has gone a long way since it was created, and title / description have not kept up. FWIW our conclusion was that we basically already require
The docs are not explicitly stating that these equivalences must hold, but it seems very much against the spirit of this trait to allow violations of these equivalences. The fact that |
@RalfJung, I find it useful to think of structures of things on a continuum where as one progresses along this continuum, additional properties must be upheld. So in this case, I'll call it the "continuum of equality" where towards one end you have notions like equivalence relations where you don't care about all the properties of equality but only those of reflexivity, symmetry, and transitivity. As you progress further, you then encounter notions of isomorphisms—or whatever the term is in that particular discipline (e.g., homeomorphisms in topology)—which have more properties associated with them than just equivalence. Since math doesn't typically have notions of "public API" or "internals", this is normally enough to model "equality"; however in set theory, one can still progress further and differentiate isomorphisms (i.e., bijections) from equality (i.e., where both sets are subsets of one another). So where along this continuum does "indiscernibility based only on the public API" lie? Maybe I am mistaken here, but I feel like in Rust the properties one must uphold in relation to a As an explicit example:
There is no way downstream code can differentiate something like 1/2 from 2/4 using the public API of Now if one decides to expose a function like Maybe this is too "niche" though. I just know that people tend to attach more properties associated with equivalence than they should since that equivalence relation really only says something about that type under that specific relation. For example, if ones knows For completeness sake, I'd argue that one can continue along this "continuum of equality" and incorporate something like spacetime and have stricter forms of equality than even structural equality where the "raw bits" are still not enough; but we are getting rather "absurd" at this point. Edit A cursory Google search led to this admittedly old Reddit discussion. As the type theorist, you likely are better equipped at formulating what drb226 and philipjf are talking about (warning: this concerns Haskell which I know is a different language with different goals than Rust). |
As discussed, I'd hope terms like "irreflexive" would not be used in the documentation since it makes no sense in general; unless you explicitly qualify it with "when |
I think "irreflexive" is still a useful term, but we should probably say explicitly that it only makes sense when Regarding "indiscernibility based only on the public API", I think I got a rough idea of what you mean, and indeed I think I have seen ways to make that more precise -- but in the interest of not further derailing this thread, I won't reply in any more detail here. :) |
I created a PR to update the docs: #85637 |
document PartialEq, PartialOrd, Ord requirements more explicitly This is the result of discussion in rust-lang#50230, in particular [this summary comment](rust-lang#50230 (comment)). Fixes rust-lang#50230.
The
PartialOrd
docs do not document what is de facto required of trait implementors in order for data structures to work correctly. We should update them!Per @RalfJung's comment below:
This post used to say something different. Here is the original post
Why doesn't
PartialOrd
require irreflexivity!(a < a)
over<
? AFAICTPartialOrd
does not actually express any kind of ordering relation for<
. For:<
: we needPartialOrd
+Irreflexivity
<=
: we needPartialOrd
+Eq
since==
must be reflexive.PartialOrd + Eq + Total
Is this an oversight? If not, we should document that
PartialOrd
requires:!(a < a)
as well. That would make
PartialOrd
's<
denote a strict partial order on<
.AFAICT this is actually what was intended, since all the discussions I can find online about
PartialOrd
talk about floats, and floats are strictly partially ordered under<
, but not non-strictly partially ordered under<=
, which would fit our current model that floats don't implementEq
.Also, the docs of
PartialOrd
do not say whatle
(<=
) andge
(>=
) actually mean. AFAICT they mean nothing forPartialOrd
in isolation. From reading the source the default implementation isa < b || a == b
, which would denote a non-strict total ordering over<=
whenEq
is implemented. But sinceEq
is not required forPartialOrd
, I'd guess that those overriding these implementations are allowed to do something else as long as they don't implementEq
. It is unclear to me what that "something else" might mean, but the docs should probably say something about this.Also, I haven't been able to find any document explaining why things are the way they are, but the fact that
PartialOrd
requiresPartialEq
makes no sense to me, and it doesn't make much sense either thatPartialOrd
exposesle
(<=
) andge
(>=
) methods, since as mentioned above, withoutEq
,le
andge
do not express any meaningful ordering relation. It seems that these traits, like all other "operators" traits, mix operator overloading with mathematical semantics: in some cases they are used to express mathematical concepts, like a strict partial ordering relation under<
, and in some other cases they are used "to provide <= for floats". If anybody has any links to any discussions / documentation about why things are the way they are I'd like to read them, and maybe we should add rationale sections to the docs.The text was updated successfully, but these errors were encountered: