Skip to content

Manually implement PartialOrd and Ord for Denomination#3934

Closed
tcharding wants to merge 2 commits intorust-bitcoin:masterfrom
tcharding:01-20-denomination-ord
Closed

Manually implement PartialOrd and Ord for Denomination#3934
tcharding wants to merge 2 commits intorust-bitcoin:masterfrom
tcharding:01-20-denomination-ord

Conversation

@tcharding
Copy link
Copy Markdown
Member

Currently units::amount::Denomination does not implement PartialOrd or Ord. This prevents any type that includes a Denomination from being used in contexts that require these traits (e.g. as key to a BTreeMap). This is an unnecessary restriction.

We had an extensive discussion in #3865 about whether the order of enum variants with no inner data matters.

This patch manually implements the traits on Denomination. Even though I started all this writing this patch feels a bit silly.

This is an alternative to #3866. I personally think we should do 3866 and not this one.

Currently `units::amount::Denomination` does not implement `PartialOrd`
or `Ord`. This prevents any type that includes a Denomination from being
used in contexts that require these traits (e.g. as key to a `BTreeMap`).
This is an unnecessary restriction.

We had an extensive discussion in rust-bitcoin#3865 about whether the order of enum
variants with no inner data matters.

This patch manually implements the traits on `Denomination`. Even though
I started all this writing this patch feels a bit silly.
tcharding added a commit to tcharding/rust-bitcoin that referenced this pull request Jan 20, 2025
In preparation for release add a changelog entry, bump the version, and
update the lock files.

`v1.0` here we come.

Before we merge this we should add:

- rust-bitcoin#3934 or rust-bitcoin#3866
- rust-bitcoin#3933
- rust-bitcoin#3932
- rust-bitcoin#3929
- rust-bitcoin#3926
- rust-bitcoin#3923
- rust-bitcoin#3893
- rust-bitcoin#3866
- rust-bitcoin#3794
@github-actions github-actions bot added the C-units PRs modifying the units crate label Jan 20, 2025
// less that bitcoin because it is in a way smaller.
fn cmp(&self, other: &Self) -> Ordering {
use Denomination as D;
use cmp::Ordering::*;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use cmp::Ordering::*;
use cmp::Ordering::{Equal, Greater, Less};

This wildcard import is what is causing the CI fail

@apoelstra
Copy link
Copy Markdown
Member

In 33fb56f:

Can you reimagine this so that it just assigns every variant a number and then we directly compare those? I doubt there will be any measurable performance impact and it will make it much clearer that the resulting order is correct and consistent.

@tcharding
Copy link
Copy Markdown
Member Author

Yeah I thought of doing that but I thought then that every reader of the enum would be like "WTF, why do these variants have this weird non-consecutive value assigned to them. Oh for <, who cares about < for an enum. These guys are off their heads"

@apoelstra
Copy link
Copy Markdown
Member

Nobody is going to think "who cares about < for an enum". And I'm not talking about giving the variants canonical numeric values. Only giving them values within the cmp function, which is currently very hard to read.

@jamillambert
Copy link
Copy Markdown
Contributor

jamillambert commented Jan 21, 2025

There is already a number assignment for each denomination in precision() which can be used for the comparison:

impl PartialEq for Denomination {
    fn eq(&self, other: &Self) -> bool {
        self.precision() == other.precision()
    }
}

impl Ord for Denomination {
    fn cmp(&self, other: &Self) -> Ordering {
        other.precision().cmp(&self.precision())
    }
}

impl PartialOrd for Denomination {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

The above gives Bit == MicroBitcoin, if that is wanted.

}

impl Ord for Denomination {
// This implementation is probably excessively precautions.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// This implementation is probably excessively precautions.
// This implementation is probably excessively precautious.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just cautious is better. "precautious" might be a word but I've never heard it before :).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes indeed, that is surely what my brain was trying to tell me. I definitely didn't mean precocious.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet, this is mad, with two grammar nerds on this team I may finally up my grammar game to a level I'm happy with.

is probably excessively precautions.

How embarrassment. (Joke based on an Australian movie almost certainly lost on non-Australians.)

"precautious" might be a word but I've never heard it before :).

I'm chalking this up as a win, I had assumed the learning new words (excluding slang) was one directional from Andrew to me.

https://duckduckgo.com/?q=precautous+def&t=canonical&ia=web

I do note however that I still misspelled it in the search.

impl Ord for Denomination {
// This implementation is probably excessively precautions.
//
// A user may one day compare `Denomination` types and assume the 'natural' order satoshi is
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// A user may one day compare `Denomination` types and assume the 'natural' order satoshi is
// A user may one day compare `Denomination` types and assume the 'natural' order where smaller denominations like satoshi are considered 'less than' larger ones like bitcoin.

@apoelstra
Copy link
Copy Markdown
Member

The above gives Bit == MicroBitcoin, if that is wanted.

We don't... in this case we want Bit < MicroBitcoin on the basis that we "fall back to alphabetical order" in case of equal units. But I like the idea of using precision otherwise. Maybe we just want to special-case this pair?

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Jan 21, 2025

Oh, crap. I see the problem. Ideally we would have BIT as associated const but that prevents us from displaying it.

I know this will be unpopular but I suspect having Eq (and consequently Ord) at all is highly misleading.

I suggest dropping the impls entirely. We can still have methods is_same_magnitude and is_displayed_same_as for equality.

@apoelstra
Copy link
Copy Markdown
Member

Honestly I'd rather drop MicroBitcoin entirely than drop the Eq impl. In some other issue Tobin was just commenting how weird uBTC is, when it really should be µ, and anyway "micro" is not a SI prefix that most people are very familiar with.

@apoelstra
Copy link
Copy Markdown
Member

Are both these units used in practice?

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Jan 21, 2025

"bit" feels more like slang while "microbitcoin" feels "formal" to me. But that might be alright if people prefer bit.

Strictly speaking, we don't need them both since people can write their own enum and manually implement displaying.

I was also thinking of having DisplayDenomination enum with different semantic and hopefully name that suggests the purpose reasonably but it seems too much. Or just rename Denomination to DisplayDenomination and don't add a new one.

@apoelstra
Copy link
Copy Markdown
Member

Strictly speaking, we don't need them both since people can write their own enum and manually implement displaying.

Yes, except that this was a bug-ridden slog for us and nobody else wants to redo that work.

I was also thinking of having DisplayDenomination enum with different semantic and hopefully name that suggests the purpose reasonably but it seems too much. Or just rename Denomination to DisplayDenomination and don't add a new one.

Display sounds like it is exclusively used by the Display trait, and not (for example) by FromStr. If you just mean that this denomination is "for displaying amounts" then Display seems redundant. What else could a denomination be used for?

@tcharding
Copy link
Copy Markdown
Member Author

With @Kixunil's point that we can add trait impls after 1.0 I'm going to chalk this up to not important enough to think about right now and leave the Denomination enum without an Ord or PartialOrd implementation.

@apoelstra
Copy link
Copy Markdown
Member

Great point. Yeah, let's not add trait impls that we don't agree on unless there's an obvious or known usecase.

@tcharding
Copy link
Copy Markdown
Member Author

tcharding commented Jan 22, 2025

And amusingly (since we are considering removing it) folk can just use ArbitraryOrd while they wait for us to implement and release Ord impls for anything that is missing them.

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Jan 22, 2025

Should we remove Eq/PartialEq until we decide what to do with bit vs microbitcoin?

@apoelstra
Copy link
Copy Markdown
Member

And amusingly (since we are considering removing it) folk can just use ArbitraryOrd while they wait for us to implement and release Ord impls for anything that is missing them.

Yes, if we are willing to put ArbitraryOrd into our API and commit to it forever.

@apoelstra
Copy link
Copy Markdown
Member

Should we remove Eq/PartialEq until we decide what to do with bit vs microbitcoin?

In my view it's obvious that they're not equal in the sense of Eq. They're different enum variants that have different names. I'm not sure when you'd want to compare enum variants for "value equality". They are a property of how an amount is displayed.

@tcharding
Copy link
Copy Markdown
Member Author

tcharding commented Jan 22, 2025

And amusingly (since we are considering removing it) folk can just use ArbitraryOrd while they wait for us to implement and release Ord impls for anything that is missing them.

Yes, if we are willing to put ArbitraryOrd into our API and commit to it forever.

Oh I mispoke. I was thinking we didn't need the ArbitraryOrd impl here for users to use ordered::Ordered<T>. I was confused (T has to implement ArbitraryOrd)

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Jan 23, 2025

In my view it's obvious that they're not equal in the sense of Eq.

Yes, the trouble is someone might forget that some variants have duplicated magnitude and they'd assume inequality implies different magnitude. That being said, I'm not sure where people would deal with that if they can just use our parsing/displaying code.

@apoelstra
Copy link
Copy Markdown
Member

Yeah, behind all this discussion is an open question about what anybody is actually doing directly manipulating the Denomination type.

tcharding added a commit to tcharding/rust-bitcoin that referenced this pull request Feb 24, 2025
We are trying a new strategy to get to 1.0 more quickly - remove
`amount` and `fee` and release everything else.

In preparation for release add a changelog entry, bump the version, and
update the lock files.

`v1.0` here we come.

Before we merge this we should add:

- rust-bitcoin#3934 or rust-bitcoin#3866
- rust-bitcoin#3933
- rust-bitcoin#3932
- rust-bitcoin#3929
- rust-bitcoin#3926
- rust-bitcoin#3923
- rust-bitcoin#3893
- rust-bitcoin#3866
- rust-bitcoin#3794
tcharding added a commit to tcharding/rust-bitcoin that referenced this pull request Feb 24, 2025
We are trying a new strategy to get to 1.0 more quickly - remove
`amount` and `fee` and release everything else.

In preparation for release add a changelog entry, bump the version, and
update the lock files.

`v1.0` here we come.

Before we merge this we should add:

- rust-bitcoin#3934 or rust-bitcoin#3866
- rust-bitcoin#3933
- rust-bitcoin#3932
- rust-bitcoin#3929
- rust-bitcoin#3926
- rust-bitcoin#3923
- rust-bitcoin#3893
- rust-bitcoin#3866
- rust-bitcoin#3794
tcharding added a commit to tcharding/rust-bitcoin that referenced this pull request Feb 24, 2025
We are trying a new strategy to get to 1.0 more quickly - remove
`amount` and `fee` and release everything else.

In preparation for release add a changelog entry, bump the version, and
update the lock files.

`v1.0` here we come.

Before we merge this we should add:

- rust-bitcoin#3934 or rust-bitcoin#3866
- rust-bitcoin#3933
- rust-bitcoin#3932
- rust-bitcoin#3929
- rust-bitcoin#3926
- rust-bitcoin#3923
- rust-bitcoin#3893
- rust-bitcoin#3866
- rust-bitcoin#3794
tcharding added a commit to tcharding/rust-bitcoin that referenced this pull request Feb 24, 2025
We are trying a new strategy to get to 1.0 more quickly - remove
`amount` and `fee` and release everything else.

In preparation for release add a changelog entry, bump the version, and
update the lock files.

`v1.0` here we come.

Before we merge this we should add:

- rust-bitcoin#3934 or rust-bitcoin#3866
- rust-bitcoin#3933
- rust-bitcoin#3932
- rust-bitcoin#3929
- rust-bitcoin#3926
- rust-bitcoin#3923
- rust-bitcoin#3893
- rust-bitcoin#3866
- rust-bitcoin#3794
tcharding added a commit to tcharding/rust-bitcoin that referenced this pull request Feb 24, 2025
We are trying a new strategy to get to 1.0 more quickly - remove
`amount` and `fee` and release everything else.

In preparation for release add a changelog entry, bump the version, and
update the lock files.

`v1.0` here we come.

Before we merge this we should add:

- rust-bitcoin#3934 or rust-bitcoin#3866
- rust-bitcoin#3933
- rust-bitcoin#3932
- rust-bitcoin#3929
- rust-bitcoin#3926
- rust-bitcoin#3923
- rust-bitcoin#3893
- rust-bitcoin#3866
- rust-bitcoin#3794
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-units PRs modifying the units crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants