1) A hint about potential prior art: F# (and/or C#?) have a first-class unit system (physical units I think), which sounds a bit similar to monetary calculations. However, physical unit are easier to model than monetary unit I think.
2) Currently, I am building something tangentially related in Rust: A backtester (test trading strategies on historical and/or simulated data) with focus on accuracy. So one thing I included already is that Assets (like etfs) are valued in a currency.
If I may be so bold: How would you approach these questions / where could I read more about that? 1) When simulating, can I always assume that the exchange will work? (For assets, that is not always the case, sometimes one has to wait a bit until there is a buyer, or there might not be a buyer at all) 2) Is there public domain data in exchange rates? 3) If I have to choose, which exchange rate should I pick? The lower one, higher? What would make the most sense in the context of trading etfs, stocks, options, crypto etc.? 4) How to approach rounding, is there a best practise? 5) I assume it is best to immediately substract taxes in every transaction, even if they are formally defined annually, right? 6) Would you model inflation? I currently plan to ignore it and present it at the very end: "Your portfolio has a final value of X.X ¥. Adjusted for inflation, that would be Y.Y ¥ today (2024-10-08)."
https://www.rebol.com/docs/core23/rebolcore-16.html#section-...
> $100 + 11 $111.00
> $10 / .50 $20.00
In general, Rebol's type system was very expressive. Would love to see more libraries like this provide that type of experience.
- Let's say I load two money values from a database. Value A is 1 USD and value B is 1 BTC. If I try to add them together, what happens? (I would expect a runtime exception. In fact, I would hope for a runtime exception, because the operation usually indicates a programming error.)
- If I divide $2.00 by 3, do I get $0.66 or $0.67? If I want to specify the rounding rule, is there a simple way to do it? I see there's support for allocation by percentage, but sometimes the allocation rules are more complex and I need to be able to specify the rounding rule.
- Does this library help parse user input? When parsing user input, what happens to extra digits? Does "0.015" get interpreted as $0.01 or $0.02?
Edit: one more question. Sometimes people bend the rules on the number of digits; for example, gas stations may charge $3.599 per gallon. Can the library deal with that or would I have to convert to decimal, multiply, and then convert back to money?
On the other hand, be careful about tying the symbol to the currency, as symbols are locale specific. For example, the symbol for USD is $ in eu-US but US$ in en-CA and en-AU (Canada and Australia), and then $US in French locales.
https://cldr.unicode.org/ is the magical dataset behind most good implementations that deal with currency display. Updated twice a year, available in JSON, providing currency symbols and formatting rules for all locales, as well as country => currency mappings and other useful information.
Disclaimer: I maintain a Go solution in this problem space: https://github.com/bojanz/currency
Tangentially, I also often think about super-strict typing. I know people mention some languages that already do this sort of thing (Units of Measure in F# was talked about). It seems silly to me that so many low level programming languages still use uint64, size_t, or char equivalents for the vast majority of the code. Why not `celsius` vs `farenheit`? Or `meters` vs `feet`. That would stop a lot of possible errors. And even more so, if the language supported things like (2 feet * 2 feet) = (4 square feet). Or (10 meters / 5 seconds = 2 m/s).
[1] https://ada-lang.io/docs/arm/AA-F/
[2] https://www.adaic.org/resources/add_content/standards/22rm/h...
[0] https://cs-syd.eu/posts/2022-08-22-how-to-deal-with-money-in...
[1] https://www.reddit.com/r/java/comments/wmqv3q/standards_for_...
[1] https://jcp.org/en/jsr/detail?id=354 [2] https://javamoney.github.io/
Edge case?
I want to calculate the installments of 265 Wei (0.000000000000000265 ETH) over a period of 144 months
The 'money/percent/usd' are almost like new keywords - which to me seems bad. Why not a formatter or another tool that simplifies but doesn't make it too clever or worse, hard to read.
Kotlin is really cool and arguably better than Java. But the language is very hard to read ('easy' to write). And with so many different ways of doing a single thing (and many of them exist to support Java, I understand) plus new 'macro-like' things expanding the language automagically makes it very hard to grasp.
I hope I am not the only one with this same feeling...
However, the Kotlin code looks absolutely… wrong? Strange? It just feels like a lot of magic to end up with ugly code.
This is all subjective, so my question is only this: is this how Kotlin is like in 2024?
Looks cool, always happy to see Kotlin love!
Parcal has a Currency type. Though, I can understand not calling it mainstream... Fun fact it's a fixed point type which is just a Int64 behind the scenes, at least in Delphi.
This looks like it's ignoring the Number and creating a new Money object (of denomination 1?)
> public infix fun Number.money(currency: String): Money = money(currency, defaultRoundingMode, null)
Visual Basic 6 and VGA had a `Currency` type (replaced by `Decimal` in VB.NET): https://learn.microsoft.com/en-us/office/vba/language/refere...
T-SQL has `money` and `smallmoney` types: https://learn.microsoft.com/en-us/sql/t-sql/data-types/money...
...am I missing something?
This one seems to carry units, as in, it is not a type with math for a known amount of fixed point precision that differentiates this library, but that it captures that plus the unit -- the currency, USD, EUR, etc -- that it is in.
- fixed point arithmetic
- tagging values with units
- localization for parsing currency names into units
I'm curious if Kotlin's type system is expressive enough to allow you to catch at compile time cases where you might be trying to, e.g. add USD to GBP.
val price = 100 money USD
Note the lack of quotes around USD.
Manipulating money is probably trickiest thing since time was invented. Library looks very usable.
I have to ask though:
> val transactionFee = 1.25.percent() // 1.5%
How is it 1.5?
I had an idea for a fixed-decimal class (that would also be useful for a money class) that held all inflight amounts as fractions and only did the division and float rounding when you needed to view the amount.
I am the author of a similar crate in the rust ecosystem: https://crates.io/crates/currencies
major features include:
* support for all ISO-4217 currencies (though not all have been explicitly tested as it is hard to find native users of some)
* compile-time macros for specifying an Amount in the native format (with symbol, etc)
* support for non-base-10 number systems (there are a few ISO currencies that needed this)
* every currency uses an appropriate backing data type, and new currencies and backing data types can be defined as long as they meet the trait requirements
* opt-in ability to enforce only checked math ops (but using the usual +,/,-,* etc symbols). This is critically important for crypto and finance applications where a panicking math op can, for example, brick a blockchain or real-time trading system
* support for parsing and printing currencies in their native format at runtime
* currencies use the appropriate format style (https://github.com/sam0x17/currencies/blob/main/core/src/cur..., i.e. symbol can be "suffix attached", "suffix spaced", "prefix attached", "prefix spaced")
* support for a number of cryptocurrencies, basically popular ones and ones I've bothered to add. Will always accept PRs adding others!
* ability to define your own currencies using the `define_currency!` macro. Though these will not be supported by the built-in `amt!` macro unless I add them to the crate.
e.g., here is how a few of the core currencies are defined:
define_currency!(USD, u64, 1_00, "$", "United States Dollar", PrefixAttached, true, false);
define_currency!(BTC, u64, 1_00000000, "BTC", "Bitcoin", SuffixSpaced, false, true);
define_currency!(ETH, U256, u64_to_u256(1_000000000000000000), "ETH", "Ethereum", SuffixSpaced, false, true);
One disadvantage right now is there is no ability to have a generic "amount of some arbitrary currency" other than through generics, as the underlying traits aren't object-safe. A good way to work around this is to define an enum that contains all the currencies you plan to support. I am working on a feature that will let you easily generate this enum at compile-time :)
parsing is done using my Quoth parsing crate which provides a very safe, lexer-less way to do parsing of UTF-8 strings that relies on recursive parsing in a way somewhat similar to syn, but there are no token streams https://crates.io/crates/quoth
I don't think that's correct, absent some no-true-scotsman gymnastics.
F# has units-of-measure (UoM) out of the box, and it supports decimal numbers. I've come across a python library for UoM as well.
The big problem with handling money in code is not, IMO, the rounding (your allocate function is a nice utility but it's not core); it's unit confusion - adding baht to ren mi bi, adding cents to euros, etc. This problem is very well solved by F#'s UoM.
I beg to differ. Java has "Decimal" class which guarantees to be safe from IEEE754 floating number side effects, and specially created to handle cases like money and financial calculations.
In these days it's used as BigDecimal, it seems [1].
[0]: https://docs.oracle.com/javase/8/docs/api/java/text/DecimalF... [1]: https://docs.oracle.com/en/java/javase/23/docs/api/java.base...
This is literally the entire point of COBOL
Is it functional , OOP or something else, which paradigm does it represent?
I always has used integer data type and counted cents. Why need this?
eth is whole number 10*18. usdc is 10*6.
usd is if to speak is 10*2 number.
solana eth price is less than eth eth price because of bridge risk.
etc.
there are on decimal money to out of crypto.
there are logarithmic money in crypto.
so many many moneys.
The use of infix functions reads a bit weird to me.
If I were to design an API like this in Kotlin, I think I would have gone for regular extensions for many cases and perhaps extension properties, think as such:
How come you went for "increaseBy" and "decreaseBy" instead of overloading `plus` and `minus`? Just curious, preference is a valid answer.