Visitar URL original
more useful sort order for multi-commodity MixedAmounts: treat missing commodities as 0 by Xitian9 · Pull Request #1564 · hledgerorg/hledger · GitHub
Skip to content

more useful sort order for multi-commodity MixedAmounts: treat missing commodities as 0 - #1564

Merged
simonmichael merged 2 commits into
hledgerorg:masterfrom
Xitian9:commoditycompare
Jul 13, 2021
Merged

simonmichael merged 2 commits into
hledgerorg:masterfrom
Xitian9:commoditycompare

Conversation

@Xitian9

@Xitian9 Xitian9 commented May 19, 2021 •

Copy link
Copy Markdown
Collaborator

Addresses #1563. When comparing MixedAmounts, only the list of Quantities is considered. Now substitutes an amount of quantity 0 for any missing commodities when doing comparisons.

@Xitian9
Xitian9 force-pushed the commoditycompare branch from 93effa3 to 4de9466 Compare May 20, 2021 00:00
@Xitian9

Xitian9 commented May 21, 2021 •

Copy link
Copy Markdown
Collaborator Author

.One ugly thing about this PR: the new Ord instance for MixedAmount can have compare a b == EQ when a /= b. I could refine the instance so it's a bit more subtle, at the cost of extra code.

Edit: Refined.

@Xitian9
Xitian9 force-pushed the commoditycompare branch 2 times, most recently from 24f961b to f3e93aa Compare May 22, 2021 01:52
@Xitian9

Xitian9 commented May 22, 2021

Copy link
Copy Markdown
Collaborator Author

Note that this differs from my proposal in #1563. It is much less ambitious in what it does, and as a result is more efficient: it does not require sorting the lists of amounts whenever doing a comparison.

@Xitian9
Xitian9 force-pushed the commoditycompare branch from f3e93aa to ef94933 Compare May 22, 2021 04:20
@Xitian9 Xitian9 changed the title Compare MixedAmounts using their list of Quantities. Compare MixedAmounts substituting 0 for missing Commodities. May 22, 2021
@Xitian9 Xitian9 changed the title Compare MixedAmounts substituting 0 for missing Commodities. Compare MixedAmounts substituting amount of quantity 0 for missing commodities. May 22, 2021
@Xitian9

Xitian9 commented May 22, 2021

Copy link
Copy Markdown
Collaborator Author

A consequence of this PR is that nullmixedamt == mixedAmount nullamt{acommodity="XXX"}, and more generally that a == b if and only if maIsZero (maMinus a b)

@Xitian9
Xitian9 force-pushed the commoditycompare branch 2 times, most recently from 6eda6f0 to 2de2b22 Compare May 31, 2021 03:12
@Xitian9
Xitian9 force-pushed the commoditycompare branch 2 times, most recently from 44bbbc3 to dd708d9 Compare June 4, 2021 10:29
@Xitian9
Xitian9 force-pushed the commoditycompare branch from dd708d9 to 14d5a22 Compare June 11, 2021 00:10
Comment thread hledger-lib/Hledger/Data/Amount.hs Outdated
compare usdneg usdpos @?= LT
compare eurneg usdpos @?= LT
compare usdneg eurpos @?= LT
compare nullmixedamt (mixedAmount $ usd 0) @?= EQ

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.

With the new Eq instance, all "zero" mixed amounts are equal, no matter how many Amounts or commodities they contain, as long as all Amounts are zero - is that right ?

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.

That sounds sensible to humans but disruptive to code, I'm surprised you haven't seen more breakage. Will a mixedAmountsReallyEqual be needed ? I guess not ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, that is correct. I have been running this for a while and haven't encountered any issues, but there are lots of opportunities for tiny problems to sneak in. If you want to be conservative we could leave the typeclass instances unchanged, and make sure we call sortBy maCompare in all the appropriate places.

compareQuantities = comparing (maybe 0 aquantity) <> comparing (maybe 0 totalprice)
totalprice x = case aprice x of
Just (TotalPrice p) -> aquantity p
_ -> 0

@simonmichael simonmichael Jul 1, 2021 •

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.

So I think sorting is done:

  1. based on quantities in all commodities, with alphabetically first commodities being more significant and missing commodities assumed to be zero

  2. if all quantities are equal, then based on the quantity of the total prices (@@), if any, treating no price or a unit price price as a total price of zero, and ignoring prices' commodities (?)

  3. if all those are equal, the sort order is undefined

Did I understand it ?

@Xitian9 Xitian9 Jul 2, 2021 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Okay, let's write this out in full detail.

A mixed amount is a list of amounts, and when we're comparing two mixed amounts we step through them sequentially. Each amount has a key which determines its precedence in the comparison. An amount's key is determined as follows:

  1. First, look at the amount's commodity. Amounts with alphabetically earlier commodities have higher precedence. If two amounts have the same commodity, then move on to the next step.
  2. Second, look at the commodity of the amount's price, if it has any. Amounts with no price have higher precedence than amounts with a price; if they do have a price, amounts with alphabetically earlier price commodities have higher precedence. If two amounts have both the same commodity and the same price commodity (or lack of price commodity), then move on to the next step.
  3. Next look at whether it's a total price or a unit price. Amounts with total price have precedence over amounts with unit price. If they have a unit price, then they are considered in order of the quantity of the unit price.

All that information goes in to determining the key of each Amount in the mixed amount. You now compare the two mixed amounts by stepping through all the amount keys in order of precedence. If a key is present in both mixed amounts, you just compare the quantities of those amounts. If a key is missing from one of the amounts, treat it as being present with quantity 0. If the keys have the same quantity, compare the quantity of their total price, if present, and treat that quantity as 0 if there is no price or if there is a unit price.

Phew…

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.

Phew is right... thanks for writing it out.

It would be good if we can present a simpler model to the user. Either different rules, or a simpler way to explain the above rules. So that it's not too difficult for a human to look at a sorted report and know why it is sorted that way.

@simonmichael simonmichael Jul 2, 2021 •

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.

PS since it touches our core data type I think this is not something for 1.22. So we can think this over.

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.

As I think you said elsewhere, we could omit some of this detail in user docs. Amount "keys" are a combination of commodity symbol and price, but generally balance reports aggregate differently-priced amounts and don't show prices, so it's good enough to just talk about commodity symbols. Except for the case where all quantities are the same and prices become significant. But probably even that can be glossed over, I doubt it's going to arise in a confusing way in practice.

And of course we're on the fringe of a fringe feature that no-one has really asked for. I think our main goal here is some kind of sensible/intuitive/explainable behaviour when people use --sort with multiple commodities. And secondly, cleaner/more principled internal design.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm happy to have this wait for after 1.22.

Most reports don't actually display prices, so the sort order only needs to look at the commodity symbol and quantity. I'm not sure if any reports both display amounts with prices and sort by amount.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's actually worth noting that this is not actually more complicated than the sorting behaviour in master. The only difference is that the behaviour in master is automatically implemented by the compiler, while our custom behaviour has to be implemented manually.

Existing behaviour:

A mixed amount is a list of amounts, and when we're comparing two mixed amounts we step through them sequentially. Each amount has a key which determines its precedence in the comparison. An amount's key is determined as follows:

  1. First, look at the amount's commodity. Amounts with alphabetically earlier commodities have higher precedence. If two amounts have the same commodity, then move on to the next step.
  2. Second, look at the commodity of the amount's price, if it has any. Amounts with no price have higher precedence than amounts with a price; if they do have a price, amounts with alphabetically earlier price commodities have higher precedence. If two amounts have both the same commodity and the same price commodity (or lack of price commodity), then move on to the next step.
  3. Next look at whether it's a total price or a unit price. Amounts with total price have precedence over amounts with unit price. If they have a unit price, then they are considered in order of the quantity of the unit price.

All that information goes in to determining the key of each Amount in the mixed amount. You now compare the two mixed amounts by stepping through the amount keys of each mixed amount. This is where the behaviour starts to differ.

  1. Look at the amount with highest precedence key in each mixed amount. If one of those amounts has higher precedence than the other, this determines the comparison of the keys. If the two keys have the same precedence, then compare the quantities of the two amounts. If those are equal, then compare whether the amounts are multipliers. If those are equal, then compare the styles. If those are equal, compare the quantity of the prices. (Plus some more comparisons on the prices which we don't care about).
  2. If step 1 was equal, then repeat it with the amounts with second highest precedence in each mixed amount, and so on.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The only place sorting by amount is used is in MultiBalanceReport, where it explicitly strips prices before sorting. So from a user-visible perspective, the price based stuff is unnecessary.

@simonmichael simonmichael added journal The journal file format, and its features. needs-impact-analysis To unblock: needs analysis of interactions with other features, users, ecosystem A-WISH Some kind of improvement request or proposal. labels Jul 1, 2021
@simonmichael simonmichael changed the title Compare MixedAmounts substituting amount of quantity 0 for missing commodities. more useful sort order for multi-commodity MixedAmounts: treat missing commodities as 0 Jul 1, 2021
@Xitian9

Xitian9 commented Jul 2, 2021

Copy link
Copy Markdown
Collaborator Author

Actually, a potential issue here that I need to look into. Now that we again only strip prices on display, we might get some weird sort orders based on prices which do not show up in the final display (c.f. #1526). We will have to make sure that sorting happens based on stripped prices where appropriate. I should come up with a test for sorting which would catch this sort of thing.

@Xitian9

Xitian9 commented Jul 2, 2021

Copy link
Copy Markdown
Collaborator Author

Actually it looks like that is already tested for! I've enhanced that test to do both flat and tree mode, and added a new test for the proposed behaviour.

@Xitian9
Xitian9 force-pushed the commoditycompare branch 2 times, most recently from ffce128 to 33d0660 Compare July 2, 2021 02:29
@simonmichael simonmichael added needs-design To unblock: needs more thought/planning, leading to a spec/plan needs-docs To unblock: needs corresponding documentation or doc updates labels Jul 2, 2021
@Xitian9

Xitian9 commented Jul 4, 2021

Copy link
Copy Markdown
Collaborator Author

How does this sound for a manual entry?

With -S/--sort-amount, accounts with the largest (most positive) balances are shown first. Eg: hledger bal expenses -MAS shows your biggest averaged monthly expenses first. When more than one commodity is present, they will be sorted by the alphabetically earliest commodity first, and then by subsequent commodities (if an amount is missing a commodity, it is treated as 0).

Revenues and liability balances are typically negative, however, so -S shows these in reverse order. To work around this, you can add --invert to flip the signs. (Or, use one of the higher-level reports, which flip the sign automatically. Eg: hledger incomestatement -MAS).

@simonmichael

simonmichael commented Jul 4, 2021 via email

Copy link
Copy Markdown
Member

@Xitian9
Xitian9 force-pushed the commoditycompare branch from c84ced8 to c9b7010 Compare July 5, 2021 08:42
@Xitian9

Xitian9 commented Jul 8, 2021

Copy link
Copy Markdown
Collaborator Author

Let me know if there's anything else I can do to get this ready for merge. And if you're just taking a break from code review that's fine too ^^.

@simonmichael

simonmichael commented Jul 8, 2021 via email •

Copy link
Copy Markdown
Member

@simonmichael

simonmichael commented Jul 8, 2021 via email

Copy link
Copy Markdown
Member

@simonmichael

simonmichael commented Jul 9, 2021 •

Copy link
Copy Markdown
Member

The basic goal of this change is to provide more predictable and intuitive behaviour when using -S/--sort-amount with multiple commodities. This is achieved by implementing a custom Ord (and Eq) instance for MixedAmount rather than relying on the derived ones. As a consequence, all the ways of representing zero with a MixedAmount ([], [A 0], [A 0, B 0, ...]) are now Eq-ual (==), whereas before they were not. We have not been able to find anything broken by this change.

There was some review and discussion with ninewise on chat.
Code, tests, docs, commitlint are looking good.
When #1597 is fixed let's check that chart with this PR.

@simonmichael

Copy link
Copy Markdown
Member

(When you get a chance, please add the above summary or something similar to the imp: commit message, will be handy for changelog.)

Xitian9 added 2 commits July 9, 2021 16:40
…ommodities. (hledgerorg#1563)

The basic goal of this change is to provide more predictable and
intuitive behaviour when using -S/--sort-amount with multiple
commodities. This is achieved by implementing a custom Ord (and Eq)
instance for MixedAmount rather than relying on the derived ones. As a
consequence, all the ways of representing zero with a MixedAmount ([],
[A 0], [A 0, B 0, ...]) are now Eq-ual (==), whereas before they were
not. We have not been able to find anything broken by this change.
@Xitian9
Xitian9 force-pushed the commoditycompare branch from c9b7010 to d2ae299 Compare July 9, 2021 06:43
@Xitian9

Xitian9 commented Jul 9, 2021

Copy link
Copy Markdown
Collaborator Author

Commit message amended.

@simonmichael
simonmichael force-pushed the master branch 8 times, most recently from 56bc295 to 01f9c70 Compare July 11, 2021 09:26
@simonmichael simonmichael removed needs-design To unblock: needs more thought/planning, leading to a spec/plan needs-docs To unblock: needs corresponding documentation or doc updates needs-impact-analysis To unblock: needs analysis of interactions with other features, users, ecosystem labels Jul 13, 2021
@simonmichael
simonmichael merged commit cf25d7d into hledgerorg:master Jul 13, 2021
@simonmichael

Copy link
Copy Markdown
Member

Looking good, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-WISH Some kind of improvement request or proposal. journal The journal file format, and its features.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants