[money] (https://hex.pm/packages/money) does exactly what you want. It is not part of the standard library because it does not need to be. This is actually a great advantage because now it can be updated independently.
But you should also consider that some currencies have specific rounding rules for cash values that relate to the physical currency in circulation.
For example, the AUD and CHF have cash rounding to 0.05 but normal rounding to 0.01.
Additionally there is the question as to when to round and to what precision. In a chain of financial transactions it is most common (according to my research, not an formal statement) that rounding is performed only at the end of the chain.
Then if you are splitting up money (like allocating it to various amounts) then rounding has to be handled carefully to ensure that any remainder is taken care of.
Anyway, money handling and its implication in the real world is an interesting problem - but its not just about decimal arithmetic (I am the author of ex_money and suggestions and PRs are always welcome)
Using a protocol, say via @Qqwy’s number library, you could easily just redefine + to call it’s protocol dispatched add function, then it would just work with, say, Decimal.
Something like 100.00 + 10 is ill defined though, I hate hate hate it when languages allow it. Do you want it to be 110.00 or do you want it to be 110 or do you want it to come out as some new type or etc… etc… That is why I like statically strongly typed languages making both operands to + be the same type, that way it is properly formed.
From a decimal and money point of view, how would you want it to manage 100.00 / 3 ? The issues of rounding, when to round, what kind of rounding all still need to be managed and the defaults are probably not what you want and expect.
Rounding is often done in the “half up” fashion - as it is for floats in Elixir/erlang, for financial transactions you probably want to use “half even”.
If you do (100.00 / 3) * 3.2351 then when should the rounding be applied? At each step or only at the end? Where would you specify the rounding in such an expression?
I’m not sure a language can apply the necessary convenience and yet be transparent on the underlying mechanisms. And given that Elixir favours being explicit over implicit I suspect this isn’t a likely focus for the core team.
Understood. But for day to day usage, how best should we handle 2-decimal exact precision numbers? If I’m working with money, then I know that 100.00 +10 is ALWAYS 110.00 - I have a context, I’m working with MONEY
This is the data you provided in your previous post. Your output gives you an error because you are expecting some kind of number, but you have a date in the “maturity-date” and a string in the “description” field. I’m not sure if this is intended or not, but it is not a number, so you should not be iterating over it as if it were.
I know its an older post but I wanted to observe that what is described by @CharlesO is basically what the Decimal package does. It’s a lovely piece of code (as you’d expect from @ericmj). It’s hard to consider using anything other than a decimal format for money types (the representation under the covers matters less - but Decimal is very tightly optimised). And as a lib writer, I find it hard to consider a money type that isn’t tagged in some way with a currency type. There’s just too many things to go wrong.
Decimal is good as the underlying type, but it doesn’t enforce handling partial unrepresentable money values, or verifying the same type of money is being compared/added/etc… That’s why the ex_money library is useful.
If you are responsible for a system that will only ever have 2 decimal point numbers and the operations involved are simply addition, subtraction, hardly ever multiplication or division, I still feel there might be some simplifications that may be obtained by not taking on a dependency.
In such a case a simplification can indeed be made, but it’s a project specific simplification and non e.g. a library or even more the core should make. The best case is always that once code is written you can change the currency without touching any of the logic working on the values. Your constraints allow simplifications, but also limit the selection of currencies to be usable. Especially the core (when we’re talking about build in stuff) should not arbitrarily limit the selection of currencies, especially if it’s just because it’s simpler to implement. If you write a library, which does so, it’s fine for as long as you communicate those limitations properly. And on a per project basis you can do what ever you want anyways. On the per project basis I would still suggest wrapping money values in a struct, which is documented to work within the given constraints and add an api to handle the manipulations on the value. If all you do is add and subtract stuff, such a module should be written quite quickly and you can skip external dependencies.
One should not try to build malformed partial implementations in any case, but even if all the currencies were having 2 decimals, the money unlike numbers require an ability to split 0.33 into two shares, than join them back and get 0.33. Otherwise the first audit would dismiss the whole business. That is impossible without very careful implementation, that is IMHO harder than anything else in the field.