[Haskell-cafe] Re: money type ?

haskell at list.mightyreason.com haskell at list.mightyreason.com
Thu Jul 12 06:46:41 EDT 2007


Donald Bruce Stewart wrote:
> simon:
>> Great - indeed,
>>
>>> sum [1.85, 5.95, -7.80]
>> 8.881784197001252e-16
>>> sum [1.85::Money, 5.95, -7.80]
>> 0.00
>>
>> I'm not yet sure these will do the best thing in all arithmetic, but it 
>> seems to be the right thing for now.
>>
>> Yes, I will need to read these also. Perhaps first reading the integer and 
>> decimal digits as separate integers will help. I'm still exploring the 
>> number types.
> 
> Roman Leschinskiy tells me that there are C (or C++?)  libraries for
> locale-specific money handling, where given precisions are mandated in
> particular countries, below which you must round. Perhaps we should have
> a binding to this.
> 
> Anyway, sorting out how money is supposed to be represented in Haskell,
> and documenting it, seems a very useful thing.
> 
> -- Don

It is funny that this thread is going on alongside the "Defaulting to Rational" 
thread.

There are separate issues with Money:
   (1) Never using Double and Float to avoid representation errors.
   (2) Provide the correct rounding at the correct moment.

For (1) you can use Data.Ratio or a pair Data.Seq of digits in base-10 before 
and after the decimal point.

For (2) you (2.a) you do not always want to round at each step of a calculation 
and (2.b) do not always want the same precision.

Example of (2.a) The 'sum' works, but what about an average price of 100 items? 
If you total then divide it might work, but if you divide like : 0.32 / 100.00 
is 0.00 then you have lost track of the money.

Example of (2.b) I have several catalogs that I order from that list prices (in 
£ or $) to higher than normal precision, e.g. "$ 0.7525", since they are often 
bought in large quantities, such as electrical components.  And exchange rates 
are also often quoted in higher than normal precision.

The "Right Thing" for storing the value is probably a Data.Ratio or a 
Data.Sequence of digits in base-10.  One might also store Currency
with a phantom type to prevent ($10 + £10) from compiling:

newtype Money cc = Money (Data.Ratio)  -- Exact, and no bias toward base 10

class Currency a where
   unit :: a              -- Smallest physical amount, e.g. $ 0.01
   nearest :: a -> a      -- Round to nearest multiple of unit
   symbol :: String
   description :: String

Then the localization (l10n) is specified by a dummy type:

data USD
data GBP

I am no l10n expert, but I could also see:

instance Show (Money USD) where ...
instance Show (Money GBP) where ...


More information about the Haskell-Cafe mailing list