« BackKotlin Moneyblog.eriksen.com.brSubmitted by eriksencosta 6 hours ago
  • eriksencosta 6 hours ago

    Manipulating monetary amounts is a common computing chore. However, no mainstream language has a first-class data type for representing money, it’s up to programmers to code abstractions for it. This isn’t an issue per se until dealing with rounding issues from operations like installment payments (e.g., buy now, pay later), foreign exchange, or even simple things like fee processing and tax collection.

    Inspired by my days at N26 Brasil dealing with these challenges, I introduce Money: a Kotlin library that makes monetary calculations and allocations easy.

    • yett 2 hours ago

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

      • nsxwolf an hour ago

        That provides the basis for solving precision and rounding issues but for a money library you need more abstractions. The ability to assign a currency code and supply conversion rates and convert between currencies at a minimum.

        • tightbookkeeper 36 minutes ago

          That’s an application design choice, not a minimum. An alternative is to work internally in one unit and convert at format time.

          • maest 18 minutes ago

            That is not a viable alternative.

            • serial_dev a minute ago

              Even though my gut reaction is to agree with you, it’s true that it is an app design decision, so in certain scenarios it might very well be a good design decision.

      • jsiepkes 2 hours ago

        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/

        • owlstuffing an hour ago

          Would be cool to hook this up as a manifold unit[1]. This way you could write code like:

          Money ask = 2.1M USD;

          What I don't see with the Java JSR is how exchange rate services are integrated (if at all?). In the best of worlds, as with manifold's units I could write.

          Money deposit = 300 USD + 500 EUR;

          Where internally the deposit automatically maintains the amounts separately and uses exchange rates only for spot display/calc, transfer, etc.

          1. https://github.com/manifold-systems/manifold/tree/master/man...

        • hathawsh an hour ago

          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?

          • eriksencosta 7 minutes ago

            Thanks for the questions. Answers:

            1. You get an exception 2. You may specify the rounding rule: https://github.com/eriksencosta/money/blob/trunk/docs/usage/... 3. It has some parsing capabilities. The extra digit is kept, rounding is only applied when doing an operation with the Money object 4. Same as 2

          • hiddew 5 hours ago

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

            • stickfigure 4 hours ago

              I'm surprised nobody has mentioned Joda Money yet:

              https://www.joda.org/joda-money/

              From the same person that brought us Joda Time (ie, what the java time API was based on). I've used Joda Money a lot and it's great.

              Honestly I prefer APIs that look like APIs and I think this trend towards inventing DSLs is a bad one. Rails works because there's a critical mass of people who have adopted what is essentially a whole new language on top of Ruby. A money library doesn't warrant a new language, it's unnecessary cognitive load. This new money library would look fine with simple constructors and method calls.

              • eriksencosta 3 minutes ago

                Joda is impressive and has great performance.

                The examples were written using the infix notation but you can just use regular method calls. For example:

                val price = Money.of(100, "USD") val shipping = Money.of(5, "USD") val subtotal = price.plus(shipping) val discount = Percentage.of(10) val total = subtotal.decreaseBy(discount)

                total.allocate(2) total.allocate(60.percent(), 40.percent())

                • nogridbag 2 hours ago

                  I personally went with Joda money versus the Java money API mentioned above. Our needs are a bit simpler and the Joda Money API is a bit simpler to understand. Our app only deals in USD so I wrote a small utility class to help initialize Money instances so devs don't have to write:

                      Money.of(CurrencyUnit.USD, amount)
                  
                  ...everywhere and do a few other things like total Money instances.
                • rafaelferreira 4 hours ago

                  Another library in this space is Eric Evans' (of DDD fame) Time & Money library https://timeandmoney.sourceforge.net/.

                • proper_elb 29 minutes ago

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

                  • wiremine 2 hours ago

                    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.

                    • eriksencosta 2 minutes ago

                      This is great!

                    • vintagedave an hour ago

                      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.

                      • getfroggie 5 hours ago

                        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.

                        • ebiester 5 hours ago

                          I think there's a good reason for it to be part of a library. The problem with currencies is much like dates: they're a social construct, and change more than most of the social constructs we embed in programming languages.

                          I don't want to update my interpreter or compiler because Turkey changed their rules of daylight savings time, or Ethereum becomes popular.

                          • JumpCrisscross 2 hours ago

                            Oh, this reminds me of my first job out of college at a Swiss bank. Apparently every time a currency conversion was done in a particular model, there was a routine that translated back and forth between the pre-Euro currency and Euro at the conversion rate. So a USD-EUR transaction with a party in France would be run as USD-FRF --> FRF-EUR. All in COBOL. As a result, every so often, you'd get a slightly different result running a USD-EUR trade with a party in France versus e.g. Germany or the U.K.

                            • tomcam an hour ago

                              So what happened when those discrepancies arose?

                              • JumpCrisscross 3 minutes ago

                                > what happened when those discrepancies arose?

                                They were just passed along.

                            • coreload 4 hours ago

                              Yes, and sometimes the context is not just social but legal or contractual, e.g. rounding currency.

                              • ebiester 4 hours ago

                                Yes, and rounding currency is just something that is always handled, as is the number of places that you take a currency out to - for example, gas is often priced at thousandths of a dollar rather than hundreds but presented to the customer in hundredths at the end. Or Yen in most cases does not have a decimal point, except that the places where you round or don't round can be consequential in large enough quantities.

                                These are largely things that can be handled by a library, but if it's in the language you best not get it wrong because it's so much harder to change!

                            • skybrian 2 hours ago

                              You could say the same of Unicode, though. Some cultural abstractions become increasingly rigid because they’re embedded in computer systems everywhere.

                              • carapace 3 hours ago

                                This. But more than being changeable they (time and money) are supra-logical or trans-rational. In other words, computers are limited to what can be modeled or calculated with the one logical operation (it has lots of names, I like "Quine dagger") but these phenomenon are not.

                              • eriksencosta 5 hours ago

                                I couldn't agree more as someone who has been using more spreadsheets than actually coding in the last 10 years.

                                • weego 3 hours ago

                                  Spreadsheets, browsers, databases.

                                  Everything is decided to be an 'abstraction' and it's someone else down the line that has to be concerned. Over the last 30 years of starting as a developer I've really lost faith that the majority are actually concerned with solving anything vs just indulging conceptual whims.

                                  • chipdart 2 hours ago

                                    > Spreadsheets, browsers, databases.

                                    I think you're mixing up stuff that's unrelated to your concern.

                                    The concerns you're expressing only apply to operations and financial transactions. That's not handled by spreadsheets, browsers, or databases. In fact, the primary concern of a browser is to provide views over data fed by servers.

                                    In addition, it sounds like you're confusing minor conveniences with something being somehow broken by design. The reason why no one bothered to standardize a money type is the fact that there isn't a technical requirements for it at all.

                                • sgt an hour ago

                                  What's the best library for JS or TypeScript?

                                  • Ygg2 5 hours ago

                                    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?

                                    • eriksencosta 5 hours ago

                                      Oh sorry, that's a typo. Thanks for pointing out!

                                    • sandGorgon 5 hours ago

                                      just curious - what is the backend api framework that N26 uses ? is it kotlin specific ? or generically spring boot or something ?

                                    • atemerev 5 hours ago

                                      Cool! As underlying values, do you use integers, bigdecimals, or a decimalized double hack like in OpenHFT?

                                      • eriksencosta 5 hours ago

                                        It uses BigDecimal. My first goal with the library was to provide a well-designed API. So to keep it simple for myself, I relied on BigDecimal for the calculations.

                                        • atemerev 5 hours ago

                                          I think you are right, API is the most important part; internal implementation can be optimized later.

                                        • vamega an hour ago

                                          What is the double hack used in OpenHFT? I tried looking this up and came up short.

                                        • psd1 4 hours ago

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

                                          • __MatrixMan__ 4 hours ago

                                            I don't know either well, but I took a glance at both and it doesn't seem like UoM is well set up for making decisions based on which peer is going to give you a better exchange rate.

                                            We sometimes pretend that money is measuring something, but in reality it's much messier than that.

                                            • chipdart 2 hours ago

                                              > (...) making decisions based on which peer is going to give you a better exchange rate.

                                              Neither does a money data type.

                                            • oblio 4 hours ago

                                              Ok, what do I do in F# if I want to not think about the low level details of FX conversions, rounding, etc? Which libraries can I use?

                                              • slekker 3 hours ago

                                                Sounds interesting! How would it look like? Do you have some code to share?

                                              • chipdart 2 hours ago

                                                > I don't think that's correct, absent some no-true-scotsman gymnastics.

                                                Yeah, I think OP's claim is not valid. Even .NET provides System.Decimal, which by design and explicitly mentions financial calculations.

                                                Taken from the intro docs:

                                                https://learn.microsoft.com/en-us/dotnet/fundamentals/runtim...

                                                > The Decimal value type is appropriate for financial calculations that require large numbers of significant integral and fractional digits and no round-off errors.

                                                • nostrademons 2 hours ago

                                                  This is often not what you want with monetary calculations. You want the rounding: you'll be making a payment of $25.86, not $25.85714285714... But you just want to make sure that the next payment is $25.85, not $25.86 or $25.857142851, and that all 7 payments together equal $181 and not $181.02.

                                                  • explorigin 2 hours ago

                                                    I can't speak for C# but the Python Decimal type handle significant figures correctly.

                                                    > The decimal module incorporates a notion of significant places so that 1.30 + 1.20 is 2.50. The trailing zero is kept to indicate significance. This is the customary presentation for monetary applications. For multiplication, the “schoolbook” approach uses all the figures in the multiplicands. For instance, 1.3 * 1.2 gives 1.56 while 1.30 * 1.20 gives 1.5600.

                                              • bayindirh 5 hours ago

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

                                                • jeremyjh 4 hours ago

                                                  Can BigDecimal tell me which currency a monetary value is denominated in?

                                                • mhluongo 4 hours ago

                                                  Tell me you've never worked in fintech without telling me you've never worked in fintech :)

                                                  Decimals aren't enough. You have frequent currency conversions and all sorts of other chores. Using a fixed-decimal datatype doesn't solve those problems by itself, it's just a tactic.

                                                  • bayindirh 4 hours ago

                                                    See JSR-354 then: https://jcp.org/en/jsr/detail?id=354

                                                    Yes, I never worked in fintech, but lots of my family members work or worked in banking sector. So, I'm not an alien when it comes to money, and how it works and processed in IT side of the things.

                                                • boronine 5 hours ago

                                                  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.

                                                  • oblio 4 hours ago

                                                    Don't we embed timezones, though?

                                                    • explorigin 2 hours ago

                                                      Timezone conversions don't change by the minute. Currency conversions do.

                                                  • shortrounddev2 5 hours ago

                                                    > However, no mainstream language has a first-class data type for representing money

                                                    This is literally the entire point of COBOL

                                                    • millerm 5 hours ago

                                                      "no mainstream language..." COBOL is has not been a mainstream language for many decades now.

                                                      • wiether 5 hours ago

                                                        It's still quite mainstream in domains where they manipulate a lot of money : banking/insurance.

                                                        The core systems of many old institutions still relies heavily on COBOL.

                                                        • eriksencosta 5 hours ago

                                                          I think I will add a footnote on COBOL. COBOL is huge in Brazil, lot of insurance/financial companies are still using mainframes.

                                                        • throw16180339 21 minutes ago

                                                          There are billions of lines of COBOL in the wild. It's not going away any time soon.

                                                    • occz 4 hours ago

                                                      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.
                                                      • sigh_again 3 hours ago

                                                        Nothing is preventing you from using it this way ? Infix functions are just syntactic sugar, some prefer it, some don't, but there's zero downsides to it (aside from your coworkers abusing it.) 5 money "USD" is literally the exact same thing as 5.money("USD"), and Int.usd = this.money("USD")

                                                        + and - are already overloaded (see val subtotal = price + shipping), increase/decreaseBy are for operating over percentages (and could be written as subtotal * (1 - discount), which is much less clear). As the other comment say, it has an actual, real life meaning that people understand clearly. Your price increased by 10 percent. the By convention is also already present in the Kotlin stdlib, although it's more for grouping operations, numeric operations are taking the Of suffix now (sumBy has been deprecated in favor of sumOf, increaseBy could become increaseOf without any loss of clarity)

                                                        • refulgentis 3 hours ago

                                                          This does a better job of showing an uneasy feeling I have about Kotlin than anything I could say.

                                                          - The infix is weird and footgun-y.

                                                          - Extension methods on int/double serving as constructors smells funny.

                                                          - Using infix operators as constructors but not using infix operators for addition/subtraction smells funny.

                                                          In general, at least in a corporate environment switching off Java for Android, I found Kotlin a distracting step sideways.

                                                          Code reviews tended to involve a lot of bikeshedding over how to make it Kotlin-y, and there's a sort of "why not?" approach to language features that creates much room for the bikeshedding.

                                                          It left me feeling like we were unconciously choosing to have the same arguments C++ programmers had in 1990, all over again. Except it was even more destructive, because those arguments were centered, and conflated with "proper" coding in the fancy new language.

                                                          I'm not against new and shiny: I was the first to use Kotlin in the org., and I dove right into Swift. There's something alarming with this transition.

                                                          I'm heartened by starting to see some debate in Android dev communities about whether Kotlin/Compose were a bridge to nowhere that shouldn't have been a focus for years.

                                                          • zoogeny an hour ago

                                                            I hear what you are saying but there is the other side. Perhaps the opinion/feeling you are having is related to getting older.

                                                            We've learned a lot about what works and what doesn't in programming languages. Goto considered harmful and all that kind of stuff. If you want to get a Lisp/Haskell fanatic really going point out how so many of the features in those languages have finally made their way into mainstream languages (lambdas, etc.).

                                                            What we don't often consider are all of the language features that didn't make it.

                                                            This process didn't stop sometime in the past. It is happening right now. That feeling of unease may not be an indication of the quality of the features you are considering. It may largely be uncertainty about what features will or will not stand the test of time.

                                                            Perhaps as we get older, we want languages that have all of the good stuff we've learned from the past and none of the experimental stuff that we aren't too sure about yet. That might be because we are starting to notice that we won't have enough time left in our remaining days to sort all the new toys into the good/bad bin.

                                                            • refulgentis 44 minutes ago

                                                              Stirring call to action for creativity, but it is unclear how a comment that boils down to "new things might be good" applies in this context.

                                                              as OP refers to, custom operators are considered harmful in languages ranging from C++ to Swift. It's a great contemporary example of goto.

                                                            • t-writescode 2 hours ago

                                                              I have used Kotlin professionally for a couple-few years; and, in my experience, infix operations are a choice and you don't have to use them. - in fact, I don't think any bit of code in anything I've used uses infix operations, this is my first time seeing them in Kotlin in the wild.

                                                              For example, I haven't used them even once.

                                                              That said, I, personally, have done almost no Android programming in Kotlin, so perhaps I'm missing the main environment where such things are done? For me, Kotlin has mostly been an aggressive amount of syntactic sugar to make Java a nice experience?

                                                              And most of the DSL-style code that I write uses function pointers as the last parameter, so I can do doTheThing(parameters) { anonymous function } and that's about it?

                                                              I'm sorry that the organization you work with has aggressive bikeshedding. Perhaps this is something your organization, in general, could have a conversation about?

                                                              • refulgentis 27 minutes ago

                                                                I agree, you nailed it. Operators are something that feels like using the new shiny but are repeating well-trodden mistakes that are well-understand in languages ranging in age from C++ to Swift.

                                                              • mrcrumb1 an hour ago

                                                                You can bikeshed in any language. Kotlin introduced a lot of optional syntactic sugar so that Java devs could choose their level of comfort with the language features. I don't really see this as a problem unless your devs are prone to this sort of bikeshedding (see: your use of "smells funny"). I've used kotlin for Android since right before it was officially supported and there has been almost no downside other than the occasional hiccup with Java/Kotlin nullability interop

                                                                • refulgentis an hour ago

                                                                  Hear hear, agree whole heartedly.

                                                                  Minor nit: bike shedding refers to, in the original, arguing about the color of the paint on the shed.

                                                                  It follows that not all discussion is bike shedding.

                                                                  In this instance, the references to C++ allude to a common best practice for programmers of avoiding custom operators, which goes far beyond an aesthetic, i.e. style, i.e. color of paint on the bike shed difference. There are engineering consequences. This also applies across languages, I'm familiar with it only from trodding the same path you are, through Swift.

                                                                  Bike shedding bike shedding is indeed possible, so I won't suggest an umabiguous definition. :)

                                                                  The bike shedding reference in OP is to the different flavors, but equivalent, syntactic sugar that you mention. This uses the new shiny. But this creates toxic baggage, because among other things, because to a naive implementer, there is no solid engineering reason to e.g. avoid custom operators, it's just a scarred C++ graybeard enforcing their opinion :)

                                                              • nwatson 4 hours ago

                                                                `decreaseBy` is a multiplication and subtraction combined, map naturally to commerce domain, and is more complex than plain addition / subtraction.

                                                              • zoogeny an hour ago

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

                                                                • bojanz 4 hours ago

                                                                  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

                                                                  • dkarl 4 hours ago

                                                                    I'm curious, is there a standard practice of library developers in a certain space collaborating across languages, sharing issues and corner cases and solutions? Is this a common practice with a name? Or is it up to you to look through issues and release notes on projects in the same space to glean useful information?

                                                                    • samatman 4 hours ago

                                                                      I know this is a mere quibble as a rider on a helpful post, but a disclosure is not a disclaimer.

                                                                      Disclaimers separate you from the comment, examples:

                                                                      > Disclaimer: I am not a lawyer and this is not legal advice

                                                                      > [says things about $company] Disclaimer: I don't work for $company, I heard this from someone who does but I can't link to a primary source

                                                                      Disclosures are additional information which you think it's proper to add, to be open about your interest or stake in the topic:

                                                                      > Disclosure: I wrote a similar library

                                                                      > [replies to thing about $company] Disclosure: I used to work for $company

                                                                      • ahoka 2 hours ago

                                                                        Disclaimer is more often used for humblebrag, not to disclose any information.

                                                                        • bojanz 4 hours ago

                                                                          Right, indeed!

                                                                      • wiseowise 38 minutes ago

                                                                        Infix functions is one of the worst features of Kotlin.

                                                                        • donjigweed 3 hours ago

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

                                                                          • Etheryte 5 hours ago

                                                                            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

                                                                            • graypegg 5 hours ago

                                                                              I think one common feature of those rounding procedures is that they are only done for cash payments, rather than plastic, so rather than representing a monetary value, it would have to represent a payment specifically. (With knowledge of payment method and time of transaction since a lot of these rules had a start date.) Possibly a good library to extend off of this!

                                                                              • eriksencosta 5 hours ago

                                                                                This is something I am aware but there is no support for this rounding scheme at the moment.

                                                                                • amluto 5 hours ago

                                                                                  Wait, how do you round? A fixed table from currency to minimum increment? You’re not about to find 1/100-Yen coins, for example.

                                                                                  • vetinari 4 hours ago

                                                                                    You are not going to find 1 or 2 eurocents anymore either, but it is still a valid amount. You can pay that electronically, but not in cash.

                                                                                    So rounding for cash is a different problem that rounding money in general.

                                                                                    • sigh_again 2 hours ago

                                                                                      >You are not going to find 1 or 2 eurocents anymore either

                                                                                      You may want to tell that to my wallet, as well as the payments I make with them. There's a cool 37 billion coins outside in the wild, so you're going to find them rather easily throughout Europe, even if your own country has stopped using them.

                                                                                      • cyxxon 4 hours ago

                                                                                        Huh? 1 and 2 eurocents have not been deprecated, afaik only Finland and the Netherlands don't use them anymore...

                                                                                        • vetinari 3 hours ago

                                                                                          Also Belgium, Ireland, Italy, Slovakia...

                                                                                          Technically, you can use them for payment, they are still valid money; but the price will be rounded to 5 cents when paying in cash and you won't get them in the other direction.

                                                                                      • criddell 5 hours ago

                                                                                        Things like gasoline can have a price that is a fraction of a cent. The ultimate price is rounded.

                                                                                        Canada has these guidelines: https://www.canada.ca/en/revenue-agency/programs/about-canad...

                                                                                  • afh1 2 hours ago

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

                                                                                    • zellyn 3 hours ago

                                                                                      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)

                                                                                      • thomascgalvin 3 hours ago

                                                                                        In Kotlin, an infix method can be called without the `.` operator, and without the parens surrounding arguments.

                                                                                            100 money "USD"
                                                                                        
                                                                                        is the same as

                                                                                            100.money("USD")
                                                                                        
                                                                                        `Number.money(currency: String): Money` is an extension method; Kotlin provides syntactic sugar which allows you to "add" methods to existing classes. The above is basically equivalent to:

                                                                                            fun money(count: Number, currency: String): Money
                                                                                        • zellyn 17 minutes ago

                                                                                          Thanks!

                                                                                          I allowed myself to be distracted by all the weird infix stuff. I was thinking "but it's not passing itself into the money constructor" but it's actually just delegating to a different-signature version of `money` _on itself_ that will use its value then.

                                                                                        • lolinder 3 hours ago

                                                                                          It'd be easier to follow if you could provide a link to the GitHub line you're looking at, but most likely the `money` function being called is a second extension method of the Number class, which means that it implicitly gets the Number object as `this` and presumably uses it to build the Money.

                                                                                      • rebeccaskinner 2 hours ago

                                                                                        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.
                                                                                        • ackfoobar an hour ago

                                                                                          > catch at compile time

                                                                                          Not in this library, but I think it's possible to put the currency as a type parameter.

                                                                                        • textlapse 3 hours ago

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

                                                                                          • dakiol an hour ago

                                                                                            Same feeling. It’s rather painful to read Kotlin code because of so many features that imho are not really that needed… and every developer wants to use them all.

                                                                                            • occz 2 hours ago

                                                                                              I think one clear tradeoff that Kotlin - arguably intentionally - makes, is that it's harder to work with than other languages without the use of an IDE. It is after all made by an IDE developer.

                                                                                              It's also quite a bit more liberal in introducing new features than Java is, and with a larger feature surface there's more potential for misuse.

                                                                                              On balance, I think using Kotlin over Java is worth it, though.

                                                                                            • Exerosis 4 hours ago

                                                                                              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!

                                                                                              • DaiPlusPlus 4 hours ago

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

                                                                                                • DaiPlusPlus an hour ago

                                                                                                  > Visual Basic 6 and VGA

                                                                                                  *VBA, sorry; typo. HN won't let me edit my posts argh.

                                                                                                  • sam0x17 4 hours ago

                                                                                                    > mainstream

                                                                                                    ;)

                                                                                                    • DaiPlusPlus 4 hours ago

                                                                                                      What could be more mainstream than VB6?

                                                                                                  • yafetn 5 hours ago

                                                                                                    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.
                                                                                                    • sigh_again 3 hours ago

                                                                                                      Maintaining an up to date currency list is, quite frankly, hell. Your code will always, always be more up to date than said list.

                                                                                                      • 946789987649 3 hours ago

                                                                                                        You could argue the same for timezones in a date library, yet they have them. I would think a library dedicated to money will in fact be the most up to date.

                                                                                                        • sigh_again 3 hours ago

                                                                                                          Do they ? I've never once had a library that stores Europe_Paris, or Offset_Plus_7_45, they've always been stringly typed. Do you have an example of who'd be crazy enough to maintain a wrapper around tzdb?

                                                                                                    • bradley13 4 hours ago

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

                                                                                                      • mplewis 3 hours ago

                                                                                                        Banks typically use a fixed floating point of 1 integer step = 1/100 cent. Is this value configurable in your library?

                                                                                                        • pasc1878 2 hours ago

                                                                                                          I doubt Japanese ones do.

                                                                                                          • jpollock 16 minutes ago

                                                                                                            Yes, because partial units still have meaning when dealing with interest and taxation.

                                                                                                        • xiaodai 4 hours ago

                                                                                                          cool. whoever uses these libraries better validated it very well.

                                                                                                          • Exerosis 4 hours ago

                                                                                                            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.

                                                                                                            • sam0x17 4 hours ago

                                                                                                              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

                                                                                                              • systems 5 hours ago

                                                                                                                what type of language is kotlin?

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

                                                                                                                • lolinder 5 hours ago

                                                                                                                  It's all of the above.

                                                                                                                  Most modern languages (including everything from Java to OCaml) don't fit neatly in any one box because they have added a bunch of features from other paradigms that make multi-paradigm programming possible. Kotlin is that way to an extreme, because instead of strapping on functional features to an OOP core like Java did it was designed out of the gate to be all of the above.

                                                                                                                  When I program in Kotlin I'm constantly shifting between "paradigms" based on what is actually needed in the moment. It's one of the best languages I've ever worked with for learning the strengths and weaknesses of different programming styles because it has such strong support for most of them.

                                                                                                                  • moritzruth 5 hours ago

                                                                                                                    Kotlin was originally designed for running on the JVM, so it is generally object-oriented, but the language and the standard library allow for and encourage a functional coding style.

                                                                                                                    Kotlin is well-suited for DSLs, especially declarative ones (see kotlinx.html[0]).

                                                                                                                    [0] https://github.com/Kotlin/kotlinx.html

                                                                                                                    • lolinder 4 hours ago

                                                                                                                      This understates how functional Kotlin is—you wouldn't say that Scala is generally OOP with some functional support just because it was built on the JVM. Scala's clearly a functional language with some OOP support.

                                                                                                                      Kotlin, in turn, is very balanced. The standard collection library uses OOP syntax (chains of method calls), but is extremely functional in its philosophy towards how we think about manipulating collections.

                                                                                                                      • umanwizard 2 hours ago

                                                                                                                        The JVM doesn't necessarily imply object-orientation. Clojure (a JVM language) is not really object-oriented at all; I'm not an expert but I think the only time objects are used in idiomatic Clojure is when doing interop with Java libraries.

                                                                                                                        • graypegg 4 hours ago

                                                                                                                          Huh, I've never actually looked too hard at Kotlin. That is a lot more syntactically flexible than I thought it was! That HTML builder reminds me of Ruby DSLs a lot.

                                                                                                                          • dtech 4 hours ago

                                                                                                                            It's not really syntactically flexible, but made explicit syntax for the use cases other languages used flexibility for, like DSL-builders and extension methods.

                                                                                                                            This avoid the problem that you have in more flexible languages where everyone does a pattern in a slightly different way.

                                                                                                                        • ragnese 5 hours ago

                                                                                                                          It's much more "multi-paradigm" or "unopinionated" than Java, but since it is a pretty thin layer over Java and uses its standard library, the ecosystem and idioms are still very much OOP by convention. But, I see the language itself as more akin to C++ in that it really doesn't strongly push much in one direction or another, but they also both lack built-in tools or optimizations for doing real functional programming. So, I'd say that Kotlin, like C++, is an unopinionated language that does well for OOP and/or imperative styles.

                                                                                                                          • beeforpork 5 hours ago

                                                                                                                            Like Java, but nicer. Used for Android app devel. Me, I would not call it multi-paradigm, because it really feels primarily like Java (though with many niceties), i.e., it is single dispatch ('this'), objects and classes everywhere. It is completely compatible with Java, and you can mix the languages freely (this is done in Android). It does have standalone functions.

                                                                                                                            • eriksencosta 5 hours ago

                                                                                                                              Kotlin is a multi-paradigm language with OOP and FP support.

                                                                                                                              • speed_spread 3 hours ago

                                                                                                                                As a Java replacement, it's still mainly an OOP paradigm with some functional bits added. The type system is mostly unchanged from Java. Kotlin's null safety is interesting, but JVM null pointers are nowhere near the problem they are in C. Otherwise its value proposition mostly relies on overcoming perceived constraints from Java syntax, allowing to redefine parts of the language to build DSLs. Whether this is a good idea is disputable; ask any maintainer of large projects where those capabilities were used in full, or just look at the continuing train wreck that is Gradle.

                                                                                                                                • g-b-r 4 hours ago

                                                                                                                                  A mess, in my limited experience, so far

                                                                                                                                • slt2021 2 hours ago

                                                                                                                                  I dont understand the need for this.

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

                                                                                                                                  • proper_elb 20 minutes ago

                                                                                                                                    More precision and/or more safety. Will depend on the domain/audience/... the software is being written for.

                                                                                                                                  • dlahoda 5 hours ago

                                                                                                                                    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.