[HN Gopher] Show HN: Kotlin Money
       ___________________________________________________________________
        
       Show HN: Kotlin Money
        
       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.
        
       Author : eriksencosta
       Score  : 319 points
       Date   : 2024-10-08 12:59 UTC (10 hours ago)
        
 (HTM) web link (blog.eriksen.com.br)
 (TXT) w3m dump (blog.eriksen.com.br)
        
       | systems wrote:
       | what type of language is kotlin?
       | 
       | Is it functional , OOP or something else, which paradigm does it
       | represent?
        
         | eriksencosta wrote:
         | Kotlin is a multi-paradigm language with OOP and FP support.
        
         | lolinder wrote:
         | 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 wrote:
         | 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
        
           | graypegg wrote:
           | 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 wrote:
             | 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.
        
           | lolinder wrote:
           | 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.
        
             | ragnese wrote:
             | I think a comparison to Scala is apt. Working with both
             | languages makes it quite clear to me that Scala is most
             | certainly much more functional than Kotlin. Kotlin is
             | really not very functional in practice, in that it really
             | doesn't encourage a functional style, nor is it optimized
             | for the patterns that are common in functional programming.
             | 
             | Scala has `Try` and `Either` for modeling domain failures
             | as values as opposed to throwing (unchecked) exceptions,
             | which are side-effecting. Scala also has for-comprehension
             | syntax built in to make it more convenient to compose and
             | chain fallible operations that use `Try`, `Either,
             | `Option`, etc. Kotlin's `Result` type is not designed for
             | modeling fallible operations (not a sealed class, no type
             | parameter on the error variant, error must be `Throwable`,
             | etc), and Kotlin does not offer convenient syntax like for-
             | comprehensions despite being more than able to (given that
             | many of us have implemented near-perfect analogues to
             | Scala's `Try` and for-comprehension syntax in Kotlin).
             | Similarly, no official Kotlin libraries or APIs _ever_
             | return errors as values and _always_ opt for throwing
             | exceptions instead.
             | 
             | Scala's collection types are implemented as persistent
             | collections, which are optimized for cheap updates. If you
             | want to avoid direct mutation in Kotlin, you have to make a
             | full copy of a collection.
             | 
             | Scala's mutable and immutable collection types are actually
             | distinct from each other and cannot be used
             | interchangeably. In Kotlin, List<T> is a supertype of
             | MutableList<T>, which means I can pass a MutableList into a
             | function that expects a List. That means that the list can
             | be changed in another thread _while_ my function is
             | running, so I can 't even assume that checking `list.size`
             | at two different points in my function will return the same
             | value.
             | 
             | Scala has actual type classes. Kotlin has extension
             | functions which are not nearly as useful (and they have
             | very surprising semantics when it comes to static vs
             | dynamic dispatch). Type classes are certainly not
             | _required_ for functional programming with a statically
             | typed language, but it definitely helps when it comes to
             | modeling things without needing to lean on writing more
             | classes and /or utilizing inheritance.
             | 
             | Also, Kotlin's "functional" APIs on collections are much
             | more janky than Scala's. For example, if I have a `Set<T>`
             | and I call `.map((T) -> R)` on it, Kotlin will give me a
             | `List<R>` while Scala will give me a `Set<R>`, which makes
             | way more sense.
             | 
             | Kotlin is cool, and it would be dishonest for me to say
             | that it's not at all functional, but after having worked in
             | other many other languages, I'm very comfortable saying
             | that Kotlin is an OOP/imperative language first with some
             | functional stuff added in (sealed classes, convenient
             | lambda syntax, top-level functions, and some of the typical
             | collection combinator APIs). Whereas Scala is quite clearly
             | designed to be actually GOOD for functional programming
             | without taking an extra-hard performance hit.
        
               | vips7L wrote:
               | Exceptions are great. Whats not great is not having them
               | expressed in the type system via checking. I think
               | Kotlin's greatest mistake is not improving upon checked
               | exception handling. Though it looks like they're going to
               | be moving forward in the future with errors as values via
               | union types [0]. Scala also has some experimental work
               | around putting exceptions into the type system via
               | capabilities [1]. I really like Scala's solution because
               | it lets checked exceptions work across higher order
               | functions and Scala has enough syntax sugar to make
               | handling exceptions pain free.
               | 
               | [0] https://youtrack.jetbrains.com/issue/KT-68296 [1]
               | https://docs.scala-
               | lang.org/scala3/reference/experimental/ca...
        
               | ragnese wrote:
               | I have LOTS of opinions about this topic, but I'll try
               | not to ramble.
               | 
               | I always found checked exceptions (Java) to be mostly
               | fine/good. I sincerely believe that a lot of the hate for
               | them in the last decade is just cargo culting. I get a
               | small dose of schadenfreude when I see someone doing
               | mental contortions to simultaneously explain why Rust's
               | Result type and handling (and similar features in other
               | langs) is awesome, and Java's checked exceptions are
               | terrible and _definitely_ not 95% similar in DX and
               | semantics...
               | 
               | The one point against checked exceptions for me is that
               | if I'm trying to model a domain failure, it really
               | doesn't make sense to collect a stack trace. For example,
               | if I'm writing a logIn function that takes a username and
               | password, then it's totally normal for the username and
               | password to be invalid. Why would I want to spend the CPU
               | time collecting a stack trace when someone simply typed
               | in an incorrect password? Do we want to collect a stack
               | trace when the password is correct and the user gets
               | logged in?
               | 
               | So, in that sense, I do have a small preference toward
               | expected failure modeling to be somehow different from
               | "true" "exceptions".
               | 
               | I do also agree with you that Kotlin's biggest original
               | sin was to throw away checked exceptions without
               | replacing the concept with ANYTHING. Of course, as you've
               | pointed out, they're backtracking on that somewhat by
               | trying to add this concept of errors as a kind of ad-hoc
               | union type. (aside: they also backtracked on their choice
               | to not have type classes because "extension functions are
               | good enough" by trying to do context receivers, which is
               | ending up being really hard and probably more complex
               | than just doing damned type classes in the first
               | place...)
               | 
               | I do kind of like the direction that Swift is moving with
               | finally adding specifically typed throw signatures
               | (essentially Swift now has checked exceptions, but
               | they're aren't actually traditional exceptions because
               | they don't collect stack traces and unwind the stack-
               | they're just syntax sugar around a Result/Try type).
               | 
               | But, my prediction is that the pendulum is starting to
               | swing back in favor of checked exceptions. In the next
               | decade we'll continue seeing languages adopt mechanisms
               | that are essentially checked exceptions. But, they will
               | be slightly different and _definitely_ called something
               | else so that we don 't have to admit that we were wrong
               | to shit on the idea for 15 years.
               | 
               | EDIT: Also, I do follow Kotlin developments closely, but
               | I haven't actually worked in Scala for several years, so
               | I had no idea about this capabilities idea. Thanks for
               | the link.
        
               | vips7L wrote:
               | I have a ton of opinions on exceptions too, mostly
               | because I love them.
               | 
               | FWIW you can override the stack trace collecting
               | behaviour of Java exceptions. Not collecting the stack
               | trace makes exceptions really fast and thats actually how
               | Scala is implementing their boundary/break feature. I do
               | kind of wish that Java could backtrack and that the stack
               | trace would only be filled in on RuntimeExceptions that
               | are true panics.
               | 
               | I really feel like Java just needs investment on the
               | language syntax to make checked exceptions good. Things
               | like `try!` or `try?` from Swift would be nice and taking
               | Scala's try { as an expression with case catch blocks
               | would make it really fluent. I think most devs can agree
               | that they want to know what errors can happen, but
               | currently in Java its just a pain to deal with them.
               | Brian Goetz originally had some ideas around evolving the
               | switch construct for this [0] so at least we know making
               | exceptions better is on his radar.
               | 
               | [0] https://openjdk.org/jeps/8323658
        
           | umanwizard wrote:
           | 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.
        
         | ragnese wrote:
         | 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 wrote:
         | 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.
        
         | g-b-r wrote:
         | A mess, in my limited experience, so far
        
         | speed_spread wrote:
         | 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.
        
       | atemerev wrote:
       | Cool! As underlying values, do you use integers, bigdecimals, or
       | a decimalized double hack like in OpenHFT?
        
         | eriksencosta wrote:
         | 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 wrote:
           | I think you are right, API is the most important part;
           | internal implementation can be optimized later.
        
         | vamega wrote:
         | What is the double hack used in OpenHFT? I tried looking this
         | up and came up short.
        
           | pcl wrote:
           | I'll byte. Perhaps you didn't look long enough. I'm sure the
           | answer will float by at some point.
        
       | sandGorgon wrote:
       | just curious - what is the backend api framework that N26 uses ?
       | is it kotlin specific ? or generically spring boot or something ?
        
         | eriksencosta wrote:
         | In Brazil (where I worked) we used Kotlin + Ktor. In Europe,
         | they are heavy Kotlin users.
         | 
         | References:
         | 
         | Brazil: https://blog.eriksen.com.br/en/platform-engineering-n26
         | Europe: https://medium.com/insiden26/engineering-at-n26-a-tour-
         | of-ou...
        
       | Ygg2 wrote:
       | 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 wrote:
         | Oh sorry, that's a typo. Thanks for pointing out!
        
       | getfroggie wrote:
       | 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.
        
         | eriksencosta wrote:
         | I couldn't agree more as someone who has been using more
         | spreadsheets than actually coding in the last 10 years.
        
         | ebiester wrote:
         | 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.
        
           | coreload wrote:
           | Yes, and sometimes the context is not just social but legal
           | or contractual, e.g. rounding currency.
        
             | ebiester wrote:
             | 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!
        
               | benatkin wrote:
               | From my perspective those are tenths of a cent. Stripe
               | has integer values for cents.
               | 
               | Am I wrong here?
               | 
               | https://stackoverflow.com/questions/35326710/stripe-
               | currency...
        
           | carapace wrote:
           | 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.
        
           | skybrian wrote:
           | You could say the same of Unicode, though. Some cultural
           | abstractions become increasingly rigid _because_ they're
           | embedded in computer systems everywhere.
        
           | JumpCrisscross wrote:
           | 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 wrote:
             | So what happened when those discrepancies arose?
        
               | JumpCrisscross wrote:
               | > _what happened when those discrepancies arose?_
               | 
               | They were just passed along.
        
               | tvaughan wrote:
               | They became the basis for the plot of Superman III
        
         | weego wrote:
         | 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 wrote:
           | > 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.
        
       | Etheryte wrote:
       | 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
        
         | eriksencosta wrote:
         | This is something I am aware but there is no support for this
         | rounding scheme at the moment.
        
           | amluto wrote:
           | 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.
        
             | criddell wrote:
             | 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...
        
             | vetinari wrote:
             | 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.
        
               | cyxxon wrote:
               | Huh? 1 and 2 eurocents have not been deprecated, afaik
               | only Finland and the Netherlands don't use them
               | anymore...
        
               | vetinari wrote:
               | 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.
        
               | sigh_again wrote:
               | >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.
        
         | graypegg wrote:
         | 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!
        
         | sernamar wrote:
         | I'm working on a Clojure library for working with money [0]
         | that handles specific rounding rules by passing a custom
         | rounding function to the round function, allowing you to
         | specify different rules as needed.
         | 
         | There's an example for the Swiss Franc in the README (the code
         | is here [1]).
         | 
         | [0] https://github.com/sernamar/dinero
         | 
         | [1]
         | https://github.com/sernamar/dinero/blob/main/src/dinero/roun...
        
       | shortrounddev2 wrote:
       | > However, no mainstream language has a first-class data type for
       | representing money
       | 
       | This is literally the entire point of COBOL
        
         | millerm wrote:
         | "no mainstream language..." COBOL is has not been a mainstream
         | language for many decades now.
        
           | wiether wrote:
           | 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 wrote:
             | I think I will add a footnote on COBOL. COBOL is huge in
             | Brazil, lot of insurance/financial companies are still
             | using mainframes.
        
           | throw16180339 wrote:
           | There are billions of lines of COBOL in production. It's not
           | going away any time soon.
        
       | dlahoda wrote:
       | 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.
        
       | hiddew wrote:
       | 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...?
        
         | rafaelferreira wrote:
         | Another library in this space is Eric Evans' (of DDD fame) Time
         | & Money library https://timeandmoney.sourceforge.net/.
        
         | stickfigure wrote:
         | 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.
        
           | nogridbag wrote:
           | 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.
        
           | eriksencosta wrote:
           | 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())
        
       | boronine wrote:
       | 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 wrote:
         | Don't we embed timezones, though?
        
           | explorigin wrote:
           | Timezone conversions don't change by the minute. Currency
           | conversions do.
        
       | bayindirh wrote:
       | > 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 wrote:
         | Can BigDecimal tell me which currency a monetary value is
         | denominated in?
        
           | bayindirh wrote:
           | No, but Java has a specification (JSR 354) to build money and
           | currency APIs [0].
           | 
           | A library built upon it can be found here [1].
           | 
           | [0]: https://jcp.org/en/jsr/detail?id=354
           | 
           | [1]: https://javamoney.github.io/
        
         | mhluongo wrote:
         | 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 wrote:
           | 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.
        
       | yafetn wrote:
       | 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 wrote:
         | 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 wrote:
           | 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 wrote:
             | 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?
        
       | sam0x17 wrote:
       | 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
        
         | eriksencosta wrote:
         | Great reference, Sam. I will definitely bookmark your library
         | and learn with your implementation.
         | 
         | For cryptocurrency data, take a look at the DTIF.org dataset:
         | https://github.com/eriksencosta/money/blob/trunk/docs/append...
        
       | xiaodai wrote:
       | cool. whoever uses these libraries better validated it very well.
        
       | occz wrote:
       | 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.
        
         | nwatson wrote:
         | `decreaseBy` is a multiplication and subtraction combined, map
         | naturally to commerce domain, and is more complex than plain
         | addition / subtraction.
        
         | sigh_again wrote:
         | 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 wrote:
         | 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.
        
           | t-writescode wrote:
           | 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 wrote:
             | 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 wrote:
           | 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 wrote:
             | 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 :)
        
           | zoogeny wrote:
           | 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 wrote:
             | 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.
        
               | zoogeny wrote:
               | I suppose the more poetic: "don't speak too soon, for the
               | wheel's still in spin" is another way to state it. It
               | isn't "new things might be good" it is more a reminder
               | that your self-described feeling of unease may say more
               | about you than it does about the language feature under
               | question.
               | 
               | I've seen some horrors in my time due to custom
               | operators. I've also suffered through some horrors when
               | they are missing and would clearly be appropriate. They
               | seem appropriate for linear algebra libraries for
               | example. Not everyone agrees, obviously.
               | 
               | As an aside, I have been watching a lot of YouTube videos
               | of people programming C. I notice that goto shows up very
               | frequently, especially in modern C. Usually it is a label
               | at the end of a function to manage resource cleanup
               | before returning, almost like a manual `defer`. Sometimes
               | it is to break out of nested loops. I mean, I've always
               | taken Dijkstra's point to be that long jumping out of a
               | procedural boundary was the real problem, not the keyword
               | "goto". And built-in language features that obviate the
               | need to use goto even for the reasonable use cases are my
               | preference.
               | 
               | I feel the same about custom infix operators. I too share
               | an unease about them. But I have to admit they feel right
               | sometimes. I admit I don't have enough information on
               | their use in this kind of case to know if it is one of
               | the good ones or one of the bad ones.
        
           | Clamchop wrote:
           | > The infix is weird and footgun-y.
           | 
           | If it's a foot gun, then I'm not seeing how the gun is
           | loaded?
           | 
           | > Extension methods on int/double serving as constructors
           | smells funny.
           | 
           | It doesn't feel meaningfully different from the static
           | factory pattern in traditional Java, where 10.percent() would
           | be something like MoneyUtils.createPercent() or
           | Percent.create() with several overloads. Under the hood, that
           | is essentially what is happening. Only downside I can see is
           | that it muddies what truly belongs to the class, but that's
           | true of any extension function and Kotlin is intended to be
           | used with IDEA's introspection anyway.
           | 
           | The Kotlin standard library has lots of extension functions
           | that construct new objects.
           | 
           | It also reminds me of Ruby, which perhaps you're also not a
           | fan of and that's OK.
        
         | gus_massa wrote:
         | > _overloading `plus` and `minus`_
         | 
         | Perhaps I'm misunderstanding your idea, but what about
         | val total = price + 10.percent * 2
         | 
         | If the price is 10, then the total is 12 or 22?
        
           | callmeal wrote:
           | >val total = price + 10.percent * 2
           | 
           | If the price is 10, I would expect total to be 10,2.
           | 
           | (price + 10.percent) * 2 would be 22.
           | 
           | I don't see any way to get 12.
        
             | ternaryoperator wrote:
             | price + (10.percent * 2) looks like 12: If 10.percent = 1,
             | 1*2 = 2. 10 + 2 = 12.
             | 
             | Under the usual rules of operator precedence would be the
             | expected answer (multiplication having higher precedence
             | than addition).
        
       | DaiPlusPlus wrote:
       | > 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?
        
         | sam0x17 wrote:
         | > mainstream
         | 
         | ;)
        
           | DaiPlusPlus wrote:
           | What could be more mainstream than VB6?
        
             | eriksencosta wrote:
             | Excel macros.
        
         | DaiPlusPlus wrote:
         | > Visual Basic 6 and VGA
         | 
         | *VBA, sorry; typo. HN won't let me edit my posts argh.
        
       | psd1 wrote:
       | > 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.
        
         | oblio wrote:
         | 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?
        
         | __MatrixMan__ wrote:
         | 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 wrote:
           | > (...) making decisions based on which peer is going to give
           | you a better exchange rate.
           | 
           | Neither does a money data type.
        
             | __MatrixMan__ wrote:
             | It looks like multiple exchange rates per currency pair is
             | supported: https://github.com/eriksencosta/money/blob/4bef9
             | 5a1d2158e308...
        
         | slekker wrote:
         | Sounds interesting! How would it look like? Do you have some
         | code to share?
        
           | williamcotton wrote:
           | Here's something along those lines in F#:
           | 
           | https://gist.github.com/williamcotton/b67ff641eeec7673131715.
           | ..
        
         | chipdart wrote:
         | > 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 wrote:
           | 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 wrote:
             | 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.
        
           | eriksencosta wrote:
           | Languages indeed have support for currencies and decimal
           | numbers. My implementation is based on Fowler's Money
           | pattern. The Money object is thus a value object representing
           | a monetary amount in a given currency.
           | 
           | https://martinfowler.com/eaaCatalog/money.html
        
       | bradley13 wrote:
       | 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)".
        
       | bojanz wrote:
       | 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 wrote:
         | 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 wrote:
         | 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
        
           | bojanz wrote:
           | Right, indeed!
        
           | ahoka wrote:
           | Disclaimer is more often used for humblebrag, not to disclose
           | any information.
        
         | eriksencosta wrote:
         | I'll introduce l10n support in a future release. My starting
         | point was to make currency names and symbols unaware of the
         | locale as I embedded cryptocurrency data in the mix.
         | 
         | Anyway, the library uses the CLDR dataset through ICU:
         | https://github.com/eriksencosta/money/blob/trunk/docs/append...
        
       | Exerosis wrote:
       | 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.
        
         | eriksencosta wrote:
         | Thanks for commenting! I'm still learning Kotlin and I
         | overlooked the usage of get() with vals. This will be the next
         | syntactic sugar into the mix. I think it makes sense.
         | 
         | When I was designing the API, I created methods like toUSD()
         | and toEUR(). But with 306+ circulating/tender currencies and
         | 2000+ cryptocurrencies, I thought it could lead to a bad
         | experience when using code completion.
        
       | Exerosis wrote:
       | 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!
        
         | eriksencosta wrote:
         | Thanks for commenting. I'm still learning Kotlin and I
         | overlooked the usage of get() with vals. This will be the next
         | syntactic sugar into the mix. I think it makes sense.
         | 
         | When I was designing the API, I created methods like toUSD()
         | and toEUR(). But with 306+ circulating/tender currencies and
         | 2000+ cryptocurrencies, I thought it could lead to a bad
         | experience when using code completion.
        
       | zellyn wrote:
       | 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)
        
         | lolinder wrote:
         | 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.
        
           | zellyn wrote:
           | https://github.com/eriksencosta/money/blob/4bef95a1d2158e308.
           | ..
        
             | lolinder wrote:
             | Yeah, that's just an infix function that provides some
             | default parameters to this extension method:
             | 
             | https://github.com/eriksencosta/money/blob/4bef95a1d2158e30
             | 8...
        
         | thomascgalvin wrote:
         | 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 wrote:
           | 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.
        
           | adamgordonbell wrote:
           | Great see that kotlin didn't avoid all Scala's bad ideas.
        
       | mplewis wrote:
       | Banks typically use a fixed floating point of 1 integer step =
       | 1/100 cent. Is this value configurable in your library?
        
         | pasc1878 wrote:
         | I doubt Japanese ones do.
        
           | jpollock wrote:
           | Yes, because partial units still have meaning when dealing
           | with interest and taxation.
        
         | eriksencosta wrote:
         | Yep, it is. However, you must create a custom currency to
         | arbitrarily define the minor units you need:
         | https://github.com/eriksencosta/money/blob/trunk/docs/usage/...
        
       | donjigweed wrote:
       | 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_...
        
       | textlapse wrote:
       | 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...
        
         | occz wrote:
         | 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.
        
         | dakiol wrote:
         | 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.
        
         | ttymck wrote:
         | > Kotlin is really cool and arguably better than Java. But the
         | language is very hard to read ('easy' to write)
         | 
         | I had the same immediate reaction. This style of code is
         | something the Scala community used to be in love with, and was
         | criticized for it. The community has largely moved away from
         | this style, so it's interesting to see history possibly
         | repeating itself with Kotlin.
        
         | vips7L wrote:
         | It feels so lambda and extension function heavy.
        
       | slt2021 wrote:
       | I dont understand the need for this.
       | 
       | I always has used integer data type and counted cents. Why need
       | this?
        
         | proper_elb wrote:
         | More precision and/or more safety. Will depend on the
         | domain/audience/... the software is being written for.
        
         | eriksencosta wrote:
         | You don't need if your solution fits your use case. The library
         | is an implementation of Fowler's Money pattern:
         | https://martinfowler.com/eaaCatalog/money.html
        
       | jsiepkes wrote:
       | 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 wrote:
         | 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...
        
           | willcipriano wrote:
           | I'd like types for all the currencies.
           | 
           | USD rent = 2000
           | 
           | EUR salary = 6000.53
           | 
           | USD disposableIncome = salary - rent
           | 
           | Then:
           | 
           | disposableIncome.toDecimal()
           | 
           | disposableIncome.toInteger()
           | 
           | disposableIncome.toString()
           | 
           | USD interest = disposableIncome.compoundInterest(startDate,
           | endDate, apy)
           | 
           | USD tip = billTotal.tip(percentage)
           | 
           | USD and EUR would inherit from Money
           | 
           | Not sure how to implement it, might be a JVM magic thing.
        
             | owlstuffing wrote:
             | What you want is Money, currency is a _unit_ type of a
             | Money quantity, just as meter is a unit type of Length.
             | 
             | Money cost = 25M USD;
             | 
             | cost += 12M EUR;
             | 
             | display(cost.to(EUR));
        
       | afh1 wrote:
       | >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.
        
       | wiremine wrote:
       | 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 wrote:
         | This is great!
        
       | rebeccaskinner wrote:
       | 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 wrote:
         | > catch at compile time
         | 
         | Not in this library, but I think it's possible to put the
         | currency as a type parameter.
        
         | eriksencosta wrote:
         | The library will throw an exception if doing an operation with
         | different currencies:
         | https://github.com/eriksencosta/money/blob/trunk/money/src/t...
         | 
         | But I think I'll make it explicit in the documentation. Thanks
         | for pointing out!
        
       | yett wrote:
       | 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 wrote:
         | 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 wrote:
           | That's an application design choice, not a minimum. An
           | alternative is to work internally in one unit and convert at
           | format time.
        
             | maest wrote:
             | That is not a viable alternative.
        
               | serial_dev wrote:
               | 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.
        
               | tightbookkeeper wrote:
               | Shipped applications disagree with you.
        
               | dmoy wrote:
               | It really depends on the context of the app
               | 
               | Sometimes you can do all calculations in a single
               | currency and convert at reporting time. I've worked on
               | teams where that was fine.
               | 
               | Sometimes... you really cannot do that.
        
               | tightbookkeeper wrote:
               | Completely agree.
        
             | Phrodo_00 wrote:
             | Might be a minimum for a money library (not completely sure
             | what the absolute minimum would be). It would be an
             | application design choice to not use one and stick to
             | general fixed point/Big Num constructs.
        
             | V41frQo1SccpfHI wrote:
             | Out of curiosity, how do you deal with changing exchange
             | rates in that case?
        
               | amoshebb wrote:
               | it depends, if it was a currency exchange, it'll be
               | labeled in one of 3 ways, either average rate per unit,
               | absolute, or it'll be a reference to a MSSQL table
               | containing exceptional spot transactions. Otherwise, a
               | complete history of exchange rates is stored in KDB, most
               | days you can take the average, and for small amounts on
               | weekends or days the exchange is closed you can just
               | interpolate, there's also a half dozen XLSXs full of
               | certain exceptional days, so you'll have to check that
               | you're not on one of those lists. Then if you're not
               | dealing with USD->X or CAD->USD->X things start to get
               | hairy.
        
             | knodi123 wrote:
             | We store all our financial data in Greenwich Mean Dollars.
        
               | irq-1 wrote:
               | What are you going to do about the new Moon Dollars?
        
           | epolanski wrote:
           | Also, avoid making errors like summing different currencies.
        
           | moomin wrote:
           | The thing is, it doesn't really help. A system I'm working on
           | can have hundreds of monetary amounts in the same currency.
           | Storing the currency against each one is an awful waste of
           | space.
           | 
           | Equally the allocation trick is good, but you'll find that
           | the people who really care about this stuff really care about
           | the exact algorithm you're using and who it favours.
        
       | sgt wrote:
       | What's the best library for JS or TypeScript?
        
       | vintagedave wrote:
       | 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.
        
         | eriksencosta wrote:
         | You are right. Languages indeed have support for currencies and
         | decimal numbers. My implementation is based on Fowler's Money
         | pattern. The Money object is thus a value object representing a
         | monetary amount in a given currency.
        
       | zoogeny wrote:
       | 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).
        
       | hathawsh wrote:
       | 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 wrote:
         | 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
        
       | wiseowise wrote:
       | Infix functions is one of the worst features of Kotlin.
        
       | proper_elb wrote:
       | 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 Y=. Adjusted for inflation, that would be
       | Y.Y Y= today (2024-10-08)."
        
         | eriksencosta wrote:
         | The currency exchange support is way simpler than this. The
         | library only provides a method that will calculate the monetary
         | amount given another Money object as the rate:
         | 
         | val amount = 1 money "USD" // USD 1.00
         | 
         | val rate = 5.4905 money "BRL" // BRL 5.4905
         | 
         | amount exchange rate // BRL 5.49
         | 
         | It's up to implementers to hook up in other datasets and to
         | consume the rates. Exchange rates datasets differ from country
         | to country and some foreign exchange companies offer short-term
         | contracts to hold a transaction at a given rate (sort of
         | "guaranteed rate/price"). I don't see myself supporting this
         | use case.
         | 
         | Nevertheless, Java Money (Moneta) has this feature. Never used
         | it so, I don't know how it works.
        
           | proper_elb wrote:
           | Thank you for your insights!
           | 
           | I think it makes sense that this feature would not be planned
           | in your library - as I understand, its goal is to support
           | developers to write better money-related logic, which is
           | sometimes related but different from simulating as accurately
           | as possible.
           | 
           | I just noticed a potential misuse of your API: Transitve
           | relationships: "A" = 2.0000 "B", and "B" = 3.0000 "C", then
           | implicitely, "A" = 6.0000 "C". Can the user now define "A" =
           | 7.0000 "C"?
           | 
           | That would be wrong - but not trivial to prevent, and
           | practically speaking, it is okay I think.
           | 
           | Thank you for your time and for this exchange, wish you good
           | success and fun with kotlin money! :)
        
       | serial_dev wrote:
       | 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?
        
       | ppeetteerr wrote:
       | 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.
        
         | foooorsyth wrote:
         | Even as someone familiar with Kotlin, it's hard to decipher
         | what this library is doing without syntax highlighting here.
         | The author is using infix functions and custom extension
         | functions on primitive types. Non-Kotlin-natives will scratch
         | heads.
        
       | kens wrote:
       | The IBM 1401 mainframe (1959) optionally supported
       | pounds/shillings/pence (PSsd) 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.)
        
         | eriksencosta wrote:
         | I love to learn more about the history of computing. Thanks.
        
       | eriksencosta wrote:
       | 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.
        
       | pmarreck wrote:
       | 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.
        
       | 0rzech wrote:
       | 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...
        
       | pbreit wrote:
       | 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.
        
         | hansonkd wrote:
         | A decimal type is sometimes orders of magnitude slower than
         | integer do simple stuff like summing (at least in Postgres and
         | Python).
         | 
         | This comes into play if you have to do aggregate functions
         | across tens of thousands of rows and can kill performance
         | compared to integers.
        
         | jacobsenscott wrote:
         | It isn't that common to need fractions though. Money _is_
         | typically handled in integer amounts (we just divide the
         | amounts by 100 or whatever to make it easier for humans to
         | grok). Nobody 's going to pay half a cent for a latte for
         | example. If for some reason you do need to deal with 1/1000 of
         | a dollar, or 1/10000 of a dollar, you can still use integers.
         | You just need to know the scaling factor.
         | 
         | Your presentation layer is probably always going to need to do
         | some manipulation of the number anyway (put a dollar sign on
         | it, etc) so using a decimal amount just for presentation needs
         | isn't a huge win. But it will work too.
        
       | xyst wrote:
       | 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
        
       ___________________________________________________________________
       (page generated 2024-10-08 23:00 UTC)