October 8th, 2024

Kotlin Money

Kotlin Money is a library for Kotlin that simplifies monetary calculations, supports traditional and cryptocurrencies, prevents rounding errors, and plans future enhancements, including Android support and improved functionality.

Read original articleLink Icon
CuriosityAppreciationConfusion
Kotlin Money

Kotlin Money is a new library designed to simplify monetary calculations in Kotlin, addressing the lack of a first-class data type for money in mainstream programming languages. The library allows developers to perform mathematical operations with monetary amounts, handle percentages, and allocate funds accurately, which is crucial for applications involving installment payments, foreign exchange, and fee processing. It supports both traditional currencies and cryptocurrencies, enabling seamless transactions and calculations. A key feature of Kotlin Money is its allocation capability, which ensures that the sum of allocated parts equals the original amount, thus preventing rounding errors that can lead to financial discrepancies. The library is built to be user-friendly, with a concise API and support for a wide range of currencies and cryptocurrencies. Future updates will aim to enhance its functionality, including support for Android development and improved persistence and serialization options. Developers are encouraged to explore the library and its usage guide for implementation in their projects.

- Kotlin Money library simplifies monetary calculations in Kotlin.

- It supports both traditional currencies and cryptocurrencies.

- The library prevents rounding errors through its allocation feature.

- Future updates will enhance functionality and support for Android.

- Developers can refer to the usage guide for implementation details.

AI: What people are saying
The comments on the Kotlin Money library reveal various insights and concerns from the community.
  • Many users are curious about handling edge cases, such as small decimals and rounding rules in monetary calculations.
  • There is a discussion about the API design, with some preferring more conventional methods over infix functions.
  • Several comments highlight the importance of distinguishing between different currencies to avoid unit confusion.
  • Users express interest in the library's internal modeling of monetary values and how it manages rounding and precision.
  • Some commenters compare Kotlin Money to similar libraries in other languages, emphasizing the need for robust currency handling across programming ecosystems.
Link Icon 50 comments
By @occz - 7 months
Cool stuff!

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:

    val fiveBucks = 5.usd
    val fiveBucks = 5.money("USD")
    val tenPercent = 10.percent
How come you went for "increaseBy" and "decreaseBy" instead of overloading `plus` and `minus`? Just curious, preference is a valid answer.
By @proper_elb - 7 months
First, congrats on the library and thank you for sharing it!

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)."

By @bojanz - 7 months
I like the support for custom currencies, as that is an edge case that often pops up.

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

By @getfroggie - 7 months
It's kind of strange that spreadsheet languages don't support money well. Using spreadsheets for escalator style automation is actually quite good and would really be amazing in a language that took typing seriously.
By @wiremine - 7 months
Nice! Reminds me of the ergonomics of Rebol's money type:

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.

By @zoogeny - 7 months
What I want more than a library is an exhaustive test suite for all of the edge cases segmented by currency or finance authority.

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).

By @Exerosis - 7 months
My only complaint is that there seems to be too much infix. Why not just do 50.btc, 25.3.usd This would keep it inline with the time API doing 20.seconds Also percentages could be standard library if you ask me but would probably also need to be 2.3.percent.

Looks cool, always happy to see Kotlin love!

By @hathawsh - 7 months
I have questions about some edge cases I've encountered in dealing with money.

- 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?

By @Etheryte - 7 months
Does this library handle rounding rules [0]? In many countries, prices are rounded to the nearest 5 cent, but the rules can often be elaborate. It looks like the allocation interface might support this, but at the moment I didn't find any mention of it without digging into the docs themselves.

[0] https://en.wikipedia.org/wiki/Cash_rounding

By @yett - 7 months
C# has a decimal type: "The decimal type is a 128-bit data type suitable for financial and monetary calculations." https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
By @donjigweed - 7 months
Nice. Looks like it satisfies all the requirements detailed here [0]. Also, good discussion of the major difficulty dealing with money here [1].

[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_...

By @jsiepkes - 7 months
Java has JSR 354 for money [1]. There is also a reference implementation [2]. So there is official support for money in Java.

[1] https://jcp.org/en/jsr/detail?id=354 [2] https://javamoney.github.io/

By @hiddew - 7 months
How does it compare to the Java money API (https://jcp.org/en/jsr/detail?id=354) and the related Kotlin DSL in https://github.com/hiddewie/money-kotlin/?tab=readme-ov-file...?
By @eriksencosta - 7 months
I'm very grateful so far by the comments. I've learned a lot and it will help me on the next iterations of the library.
By @Ygg2 - 7 months
Nice library!

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?

By @textlapse - 7 months
Kotlin has the problem of "I had N problems, so I think I can create 1 new solution, now I have N+1 problems" (obligatory https://xkcd.com/927/).

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...

By @0rzech - 7 months
Not a mainstream language, but Ada has Annex F - Information Systems, which can be used for currency handling. [1][2]

[1] https://ada-lang.io/docs/arm/AA-F/

[2] https://www.adaic.org/resources/add_content/standards/22rm/h...

By @DaiPlusPlus - 7 months
> no mainstream language has a first-class data type for representing money

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?

By @kens - 7 months
The IBM 1401 mainframe (1959) optionally supported pounds/shillings/pence (£sd) arithmetic in hardware. In those days, there were 12 pence in a shilling and 20 shillings in a pound, so arithmetic with UK money was non-trivial. (This wasn't even microcode; this was literally boards full of transistors to add, subtract, multiply, divide, and format currency values.)
By @zellyn - 7 months
I'm new to Kotlin. Can someone explain how this function creates a Money object incorporating the given count of them?

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)

By @wiseowise - 7 months
Infix functions is one of the worst features of Kotlin.
By @serial_dev - 7 months
Congrats on the library, I’ll be looking into it for some nuggets of knowledge.

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?

By @afh1 - 7 months
>However, no mainstream language has a first-class data type for representing money

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.

By @yafetn - 7 months
The currency codes could probably be inline value classes. That way, you can do

    val price = 100 money USD
Note the lack of quotes around USD.
By @ppeetteerr - 7 months
What is up with that API? `1.25.percent()`? `1 money 'USD'`? Love the spirit of a money library, but this is a little odd.
By @pmarreck - 7 months
This covered the API nicely but said nothing about how the amounts were internally-modeled and how rounding was dealt with. It was only mentioned.

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.

By @rebeccaskinner - 7 months
Neat. It seems to me like this is filling three separate gaps:

  - 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.
By @xyst - 7 months
I wonder if this would handle small decimals.

Edge case?

I want to calculate the installments of 265 Wei (0.000000000000000265 ETH) over a period of 144 months

By @mplewis - 7 months
Banks typically use a fixed floating point of 1 integer step = 1/100 cent. Is this value configurable in your library?
By @vintagedave - 7 months
Delphi has a currency type, as does C++Builder (so, a native Currency type available in C++ with that toolchain.)

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.

By @pbreit - 7 months
Are integers still a preferred method for working with and storing monetary amounts or can we go back to decimals now that most languages handle decimals decently? Monetary amounts pretty much always need to eventually be presented to humans so handling them as integers is quite a pain.
By @atemerev - 7 months
Cool! As underlying values, do you use integers, bigdecimals, or a decimalized double hack like in OpenHFT?
By @monkaiju - 7 months
I just had to implement the 'allocate' functionality described here, specifically with the %-based weights, and it was a pain that required careful comments to ensure was clear! Would've loved to use this instead.

It was fun writing the extra pennies distribution logic tho

By @bradley13 - 7 months
Look nice. I do find the wordy operators reminiscent of Cobol. Instead of "subtotal decreaseBy discount" in Kotline I would expect either "subtotal.decreaseBy(discount)" or perhaps "subtotal * (1 - discount)".
By @endgame - 7 months
I don't know Kotlin very well. Can it track the currencies involved at the type level, so that trying to combine (say) USD and EUR amounts in an error?
By @Exerosis - 7 months
My complaint is, bit too much infix :( What about 50.btc 25.usd Keeps it inline with the time API as well with 20.seconds and what not.
By @sandGorgon - 7 months
just curious - what is the backend api framework that N26 uses ? is it kotlin specific ? or generically spring boot or something ?
By @xiaodai - 7 months
cool. whoever uses these libraries better validated it very well.
By @sgt - 7 months
What's the best library for JS or TypeScript?
By @sam0x17 - 7 months
This is cool and it's great to see people adding better first-class support for currencies in as many languages as possible!

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

By @psd1 - 7 months
> no mainstream language has a first-class data type for representing money

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.

By @bayindirh - 7 months
> However, no mainstream language has a first-class data type for representing money...

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...

By @shortrounddev2 - 7 months
> However, no mainstream language has a first-class data type for representing money

This is literally the entire point of COBOL

By @boronine - 7 months
I think most of this is covered by a good Decimal API, currency stuff probably shouldn't be embedded into a language because it changes: currencies come and go, get redenominated etc. Although one simple thing that would be useful is keeping track of abstract units, e.g. throwing an error when attempting to do 10 USD + 10 EUR.
By @systems - 7 months
what type of language is kotlin?

Is it functional , OOP or something else, which paradigm does it represent?

By @slt2021 - 7 months
I dont understand the need for this.

I always has used integer data type and counted cents. Why need this?

By @dlahoda - 7 months
crypto needs support for decimals, determinism, rounding directions, uplifting to higher dimensions during long term accrual, down lifting fosome kind of quantization, path dependance.

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.