prePEP: Money data type

Ian Bicking ianb at colorstudy.com
Mon Oct 20 00:33:50 EDT 2003


On Sunday, October 19, 2003, at 03:24 AM, Alex Martelli wrote:
> Ian Bicking wrote:
>    ...
>>> So you think y=x/2 should not work when x is an instance of Money,
>>> nor should y=x*z when x is an instance of Money and z is e.g. 0.5?
>>> That seems pretty limiting to me.
>>
>> I could go either way.  Or rather I'd like both.  If I was doing
>> serious accounting programming I *absolutely* would want money*0.5 to
>> raise an exception -- I would want to be very explicit about anything
>> that had the potential of being imprecise.  It would simply be too 
>> easy
>> to mess this up otherwise.
>
> There is no "potential of being imprecise" when I divide a money amount
> by two: it just may need rounding according to the applicable rules if 
> it
> happens to result in a half cent (or whatever other least significant 
> unit).
> If, as you suggest and opposite to the ideas from the Fowler article 
> you
> quote, Money instances DON'T carry information about the rules of 
> rounding,
> then it's that lack of information which may create problems.
>
> Note that "potential of being imprecise" (by one cent, and as long as
> the EU rules are respected) is specifically allowed in EU regulations,
> and they also recommend the 1-cent tolerance be built into data
> processing equipment and programs.  I.e., they specifically state that
> such 1-cent discrepancy is NOT legal grounds for dispute, as long as
> it follows from computations respecting The Rules; and recommend
> that accounting programs accept that automatically, pointing out that
> reconciliation of accounts would otherwise be cumbersome.  Apparently
> that's one more difference between European and American approaches
> to accounting arithmetic?
>
> So, in your vision of serious accounting, if I needed to multiply a 
> Euro
> amount by 1936.27 (or any of the many other legally mandated rates)
> I could not just multiply by 193627 then divide by 100?  Would there
> have to be a "precise decimal" type _in addition_ to the Money type to
> allow such computations?  (Multiplying money by money seems to make
> no dimensional sense -- those rates are dimensionally just numbers,
> unless perhaps one's "Money" includes currency denomination, in which
> case they can dimensionally be "ratios of currencies").  But these
> computations would need rounding rules too; and sticking the rules
> with the "precise decimal" when obviously they depend not on the
> number, but on the kind of Money (is it European, American, ...?),
> seems truly weird to me.
>
> I shudder to think of doing e.g. present value computations on a cash 
> flow,
> let alone seriously complicated arithmetic, in a system where I can't 
> just
> use normal arithmetic operations.  Having to boilerplately call
>     x.divide_by(y, money.rounding_european)
> each and every time I mean
>     x/y
> BECAUSE for reasons totally unclear to me x isn't allowed to carry
> the rounding spec, even though all computations on x must use that
> spec, would drive me away in no time.

Money could carry the rounding policy -- I don't really know if that's 
right or not, I'm not convinced either way.  But I'm not sure it's 
really "money" if it carries a rounding policy.  A rounding policy 
isn't intrinsic to the notion of money.  EU has a rounding policy, but 
Euros don't -- if we're dealing with money in Euros in the US, the 
rounding policy may be different.  It may be radically different -- 
where x/3 might work with the EU policy, other accounting systems might 
require "v1, v2, v3 = x.divide(3)", so that pennies aren't lost.  I 
don't see how a rounding policy can handle that, unless you intend a 
lowest-common-denominator.  But that denominator gets lower and lower 
as you generalize, so it never works out.

If you include a rounding policy you are really specifying an 
accounting entry.  That "money" cannot be put into other accounts 
without checking and converting the rounding policy.  But the rounding 
policy may not be the only policy -- I don't have experience to know of 
other circumstances, but it's easy enough to imagine other kinds of 
policies that belong to entries.

If that's common, then you probably will want a formal "entry" object, 
which would contain a money object.  At that point the rounding 
information held by the money object will be redundant and perhaps a 
hinderance.

I prefer the idea of wrapping money in domain-specific ways, and 
leaving the money object relatively pure.

>> If I was a more casual user of Money (which is actually all that I'm
>> likely to ever be) then I would be annoyed by that preciseness.
>>
>> In this circumstance, it would be reasonable for me to make my own
>> money subclass to use in my application, if I wanted to avoid rounding
>> errors.  Or, if an option were available for forcing explicit 
>> rounding,
>> that would also work.  (But in most cases I wouldn't suggest
>> subclassing as a feasible means of customization, e.g., for defining a
>> standard rounding method -- subclassing for customization sucks)
>
> If you have to customize, subclassing is a decent way to do so.  But
> I don't see the need to customize, and thus to subclass, here, when
> we know in advance that most every computation with money will need
> to be able to perform rounding according to certain rules.  Sure, ALLOW
> customization by subclassing for potential need for "super-weird" 
> rules --
> expose the .round method that is internally called to do rounding that
> depends on the rules, and if I have to follow ones outside the small 
> set
> that we can predict in advance, then the requirement to subclass is not
> too onerous.  But normal cases should be in the Money class!

Agreed.  The one kind of customization I might consider is if you 
wanted to, application-wide, enforce coding standards, like no implicit 
rounding.  You could subclass and replace the money class for the 
entire application.  It's kind of hackish, but it should actually work 
okay for this particular case.  Most other kinds of customization don't 
work well, because library code will usually use uncustomized classes.  
Especially with immutable instances, where library code will return new 
objects instead of modifying your customized object in place.

>>>> No, the only places where repr() round trips is for Python literals.
>>>
>>> Not true:
>    ...
>> Okay, what I said was not entirely true.  I was just concerned that 
>> the
>> repr() of a money object *must* be readable -- sacrificing readability
>> for accurate round-tripping is not acceptable.  It seemed like John 
>> was
>> suggesting that.  repr() should not take the place of pickle.dumps()
>
> Guaranteed readability is the business of str().  repr() must be 
> complete
> and accurate even if that sacrifices readability.  Round-tripping 
> cannot
> generally be guaranteed for many types, but when it can it's a sensible
> way to double check on completeness and accuracy; readability must
> not be allowed to interfere with completeness and accuracy.  Sure, 
> pickle
> is there for those times where round-tripping is too onerous on other
> grounds, but readability is not one of those grounds -- otherwise what
> do you think str() is there FOR?

That's not a realistic idea of repr() -- repr() doesn't need to be 
accurate, because it usually isn't!  Anyone who expects repr() to be a 
accurate way to reconstruct objects isn't just wrong, they aren't even 
paying attention.

repr() is generally for debugging purposes.  It's a summary of an 
object where completeness is valued over aesthetics (contrary to 
str()).  It's not necessarily a complete picture of an object.

--
Ian Bicking | ianb at colorstudy.com | http://blog.ianbicking.org






More information about the Python-list mailing list