Skip to content

Instantly share code, notes, and snippets.

@Geolykt
Created July 10, 2023 17:56
Show Gist options
  • Save Geolykt/2297f64284eb4b744917d971d79f1f80 to your computer and use it in GitHub Desktop.
Save Geolykt/2297f64284eb4b744917d971d79f1f80 to your computer and use it in GitHub Desktop.
Manifesto for multi-currency systems without primary currencies
In a world where there isn't just one but multiple currencies, one has an acute problem of knowing what economy to use at all times. You can't just transfer 500 from A to B. You need to order the economy implementation to transfer 500 of currency C from A to B.
At first glance this may sound like not of an issue, until you realize that most inputs will be done in purely scalar values. The user will enter `/give minecraftplayer2 500`, not `/give minecraftplayer2 500 virtualGoldNugget`. Similarly all configuration files will probably only have a scalar value, but not a currency value.
The solution: Continue using scalars everywhere, where as the scalars values are implicitly assigned to a fallback currency. However, given that a not so small amount of API consumers will continue to only use that scalar system (after all, this is the system we all grew up with thanks to vault), one can say that this fallback currency is in fact a primary currency.
However the problem you seem to be oblivious to, is that in doing so you just recreated the old monolithic scalar-only currency system. What is the point of *supporting* multiple currencies if there is no consumer to actually use them?
Sure, you may believe that you can just use Currency#parse as per <https://github.com/ArcanePlugins/Treasury/blob/07cecf40b4263c3a674d484c5663c6b5d4b96f50/api/src/main/java/me/lokka30/treasury/api/economy/currency/Currency.java#L177>. However, implementations may expect that the method was called on this instance for good reason and as such may be overly lenient in their parsing (`1.0` and `1,0` and `$1` and `1$` and `1 $` and `1 USD` and `USD 1` being all the same for example). Once the implementation is so lenient that it accepts pure-scalar strings it is over. You now cannot effectively loop over each registered Currency and call #parse on them as multiple different Currencies could return the same thing due them being overly lenient.
The alternative? Well, one could just force the user to make use of the currencies identifier. But the identifier is not meant to be humanly readable and thus we are at square one.
However, at it's core the idea of making use of Currency#parse to differentiate between currencies is in it's own not wrong - albeit being expensive thanks to using exceptions as flow control. The only issue with implementations being too lenient can be resolved on paper with javadoc changes. However, in doing so we also fixed the root issue of our scalar problem. There is now no longer a need for a dedicated primary currency - instead a currency that just happens to only accept scalars as input will take it's place as an ordinary currency that is API-wise not differentiable from other currencies.
I am well aware that the window for ABI breakages is long over. However, fixing the scalar currency problem is rather straight-forward. First of all all Currency#parse needs to be explicitly reworded so that leniency is not an option; that being said I do not know how to concretely enforce it at compile-time to say the least - running a quick leniency test at runtime to ensure that at most only one currency accepts scalar values is an option, but wouldn't catch all violators. Furthermore, references to #getPrimaryCurrency need to be discouraged - this can be easily done via a simple deprecation, it doesn't have to be fancy. Third of all there should perhaps be a helper method to quickly determine which currency is attached to any given string. That helper method could also adopt some of the leniency tests I mentioned before - that is it will fail if more than two currencies claim to be able to parse a given value. This however may cause behaviour that can be considered to be bugged for the end user and will also increase the computational power required to parse currencies and their amount from strings.
On the bright side this will cause all plugins to drop their antiquated scalar-only behaviour in the long run, effectively paving the way to a world in which all API consumers make full use of a multi-currency system.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment