-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Ordering::and_then #1677
Comments
Bikeshed: To me |
@ExpHP To add to the similarity, this is also the behavior of javascript's |
Is there any update on this? I'd also like to have this feature, can I contribute somehow? @digama0 This would also be similar to Haskell's monoid instance for Data.Ord.Ordering. |
@rednum I like that, that's a clever use of monads. (I assume you meant monad not monoid which is a very different algebraic structure.) |
@rednum These seem like reasonable APIs that match conventions, so it should be fine to just add them (marked unstable) and open a PR against rust-lang/rust! |
@digama0 myComparator self other =
(name self `compare` name other) `mappend`
(address self `compare` address other) @sfackler impl Ordering {
pub fn then_cmp(self, o1: &Ord, o2: &Ord) -> Ordering {
match self {
Ordering::Equal => o1.cmp(o2),
o => o,
}
} so that you could write self.name.cmp(other.name).then_cmp(self.address, other.address) |
@rednum That's functionally equivalent to self.name.cmp(other.name).and_then(|| self.address.cmp(other.address)) |
That could be convenient, yeah, though I'd personally lean towards just |
Why not just use chain as the name? The conjunctions really have to fit perfectly, otherwise I wouldn't use them. |
I think |
Add or and or_else for ordering. Fixes #37053 (see discussion in rust-lang/rfcs#1677).
This function is a bit awkward to use in implementations of For example, let's take a simple tuple for
That's pretty simple, but:
Note that you can't easily do a Perhaps there should be a version of these functions that works with |
@clarcharr I guess it needs to be something else than usize to be relevant, something that doesn't impl Ord, you mean? Almost seems like a job for |
@bluss I used Perhaps |
Hmm, even with fn partial_cmp((a1, a2): (f64, f64), (b1, b2): (f64, f64)) -> Option<Ordering> {
let c = try_some!(a1.partial_cmp(b1));
let c = c.or_else(|| try_some!(a2.partial_cmp(b2))); // <-- whoops, returns None in the closure!
Some(c)
} |
On this I fear there may be multiple reasonable definitions of As an example of another definition: rednum mentioned above that in Haskell,
So |
@ExpHP although Haskell does this, this isn't how Rust does it. For example, take this code: https://is.gd/G2TNGV
Prints Which makes sense IMHO to be the only accepted interpretation, but there could be arguments in the other direction. |
Ah, I see now, there is indeed a definition which is already very-well established in existing With small modifications to your implementation above to get it to compile: (the most significant difference being the addition of an fn posted_partial_cmp((t11, t12): (f64, f64), (t21, t22): (f64, f64)) -> Option<Ordering> {
t11.partial_cmp(&t21).and_then(|c1| {
if let Some(c2) = t12.partial_cmp(&t22) {
Some(then(c1, c2))
} else { None }
})
} its output actually differs subtly from the standard implementation (see https://is.gd/rM8cSN) in cases where the second comparison fails, and I see no easy/composable fix (tables are arranged as in my prior post):
Meanwhile, it is apparent that much like impl Option<Ordering> {
// modulo heavy bikeshedding
pub fn and_then_partial<F>(self, other: F) -> Option<Ordering>
where F: FnOnce() -> Option<Ordering> {
match self {
Some(Ordering::Equal) => other(),
c => c,
}
}
pub fn then_partial(self, other: Option<Ordering>) -> Option<Ordering> {
match self {
Some(Ordering::Equal) => other,
c => c,
}
}
} I wholeheartedly agree that functions that represent this well-established convention for |
Sounds good to me! I would probably name them |
One other quick note on the way I conceptualised this: The monoid version from Haskell makes sense within the way I understand |
I think the current choice makes sense in the context of lexicographic comparison, which is how One should also consider the behavior for infinite iterators. If Some might consider the current behavior a footgun, but it really depends on the application; the current behavior is in line with existing (and unavoidable) behavior on A similar argument is that the current definition permits local reasoning; one can say that
|
These were added as |
Just as a peculiar data point on the name, when I saw my first comment here again today, I couldn't believe that I had suggested I think that at the time I wrote that post, I was primarily coding in Haskell, so the first thought that popped into my head was probably how I might implement that using the Alternative instance for Maybe (guess what that does). In contrast, my view coming in today has been colored by lots of shell scripting, where success is neutral. Best to just stay away from the names "and" and "or" entirely. |
Add or and or_else for ordering. Fixes rust-lang/rust#37053 (see discussion in rust-lang/rfcs#1677).
From rust-lang/rust#34774:
This seems like an obvious and missing function from
std::cmp::Ordering
:This is useful for creating comparators which do lexicographic order. The second variant allows for skipping the order calculation if it is not necessary.
To explain the name
then
/and_then
: Java has a similar function for comparators, calledthenComparing
. The idea is that you can read a comparison function likeas "compare by name, then by address". The
then
/and_then
difference with a closure is inspired by Rust's function names forOption
likeor
(by value) /or_else
(by closure).The text was updated successfully, but these errors were encountered: