Repository navigation
more useful sort order for multi-commodity MixedAmounts: treat missing commodities as 0 - #1564
Conversation
|
Edit: Refined. |
24f961b to
f3e93aa
Compare
|
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. |
|
A consequence of this PR is that |
6eda6f0 to
2de2b22
Compare
44bbbc3 to
dd708d9
Compare
dd708d9 to
14d5a22
Compare
| compare usdneg usdpos @?= LT | ||
| compare eurneg usdpos @?= LT | ||
| compare usdneg eurpos @?= LT | ||
| compare nullmixedamt (mixedAmount $ usd 0) @?= EQ |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
So I think sorting is done:
-
based on quantities in all commodities, with alphabetically first commodities being more significant and missing commodities assumed to be zero
-
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 (?)
-
if all those are equal, the sort order is undefined
Did I understand it ?
There was a problem hiding this comment.
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:
- 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.
- 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.
- 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…
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
PS since it touches our core data type I think this is not something for 1.22. So we can think this over.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
- 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.
- 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.
- 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.
- 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).
- If step 1 was equal, then repeat it with the amounts with second highest precedence in each mixed amount, and so on.
There was a problem hiding this comment.
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.
|
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. |
|
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. |
ffce128 to
33d0660
Compare
|
How does this sound for a manual entry? With Revenues and liability balances are typically negative, however, so |
|
It sounds good!
|
|
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 ^^. |
|
Will do. Yes I'm rationing out large/tricky-PR-code-review time, it is costly. Sorry for the delay.
|
|
PS other devs can help by contributing code review! (I'm very pleased that this is starting to happen.)
|
|
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. |
|
(When you get a chance, please add the above summary or something similar to the imp: commit message, will be handy for changelog.) |
…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.
|
Commit message amended. |
56bc295 to
01f9c70
Compare
|
Looking good, thank you! |
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.