glecimal/money
An amount of money: a decimal held at exactly its currency’s minor
units, so BRL 10.50 always has two decimals and JPY 1050 none.
Money never mixes currencies (adding BRL to USD is
CurrencyMismatch), its own arithmetic is exact, and a value with
nonzero digits beyond the currency’s minor units comes in only through
new_rounded, multiply or convert, each with a named rounding.
Allocation works in minor units, so the parts always add up to the whole.
import glecimal.{HalfEven}
import glecimal/currency
import glecimal/money
pub fn invoice() -> Result(List(money.Money), money.MoneyError) {
use price <- result.try(money.from_minor_units(19_990, in: currency.brl))
use rate <- result.try(
glecimal.parse("0.0725") |> result.map_error(money.InvalidAmount),
)
use tax <- result.try(money.multiply(price, by: rate, rounding: HalfEven))
use total <- result.try(money.add(price, tax))
money.split(total, into: 3)
}
Types
Why a money operation has no result.
pub type MoneyError {
CurrencyMismatch(
expected: currency.Currency,
found: currency.Currency,
)
Arithmetic(glecimal.ArithmeticError)
InvalidAmount(glecimal.ParseError)
UnknownCurrencyCode(code: String)
MalformedText
}
Constructors
-
CurrencyMismatch( expected: currency.Currency, found: currency.Currency, )The operation combined two currencies.
-
Arithmetic(glecimal.ArithmeticError)The arithmetic or allocation failed, for example
WouldRoundwhen an amount has nonzero digits beyond its currency’s minor units. -
InvalidAmount(glecimal.ParseError)parseread an amount it could not accept; positions count from the start of the whole text. -
UnknownCurrencyCode(code: String)parseordecoderread a code that is not in ISO 4217. -
MalformedTextparseread a text that is not a code, one space and an amount.
Values
pub fn allocate(
money: Money,
weights weights: List(glecimal.Decimal),
) -> Result(List(Money), MoneyError)
Splits the amount in proportion to weights, in minor units, so the
parts add up to it exactly; see glecimal.allocate.
pub fn compare(
a: Money,
b: Money,
) -> Result(order.Order, MoneyError)
Compares two amounts in the same currency.
pub fn convert(
money: Money,
to target: currency.Currency,
at rate: glecimal.Decimal,
rounding rounding: glecimal.Rounding,
) -> Result(Money, MoneyError)
money in another currency at rate, the units of the target currency
per unit of money’s, rounded once to the target’s minor units:
BRL 199.90 converted to currency.usd at 0.1834 is USD 36.66.
pub fn currency(money: Money) -> currency.Currency
pub fn decoder() -> decode.Decoder(Money)
Reads {"amount": "1234.50", "currency": "BRL"}: the amount as a string
(numbers are refused) and the code in ISO 4217. A refusal names its
reason, at the path of the field that caused it.
pub fn describe_error(error: MoneyError) -> String
A sentence for logs and messages.
pub fn format(
money: Money,
notation: notation.Notation,
) -> String
The amount for display, in notation and without a currency symbol:
format(price, notation.pt_br) is "1.234,50" for BRL 1234.50.
pub fn from_minor_units(
units: Int,
in currency: currency.Currency,
) -> Result(Money, MoneyError)
An amount counted in minor units: from_minor_units(1050, in: currency.brl) is BRL 10.50.
pub fn is_negative(money: Money) -> Bool
pub fn is_positive(money: Money) -> Bool
pub fn multiply(
money: Money,
by factor: glecimal.Decimal,
rounding rounding: glecimal.Rounding,
) -> Result(Money, MoneyError)
money × factor (a quantity, a rate, an exchange rate into the same
currency), rounded once to the currency’s minor units, whatever the
factor’s scale.
pub fn new(
amount: glecimal.Decimal,
in currency: currency.Currency,
) -> Result(Money, MoneyError)
amount in currency, exactly. Zeros are added up to the currency’s
minor units; a nonzero digit beyond them is Arithmetic(WouldRound).
pub fn new_rounded(
amount: glecimal.Decimal,
in currency: currency.Currency,
rounding rounding: glecimal.Rounding,
) -> Result(Money, MoneyError)
amount in currency, rounded to the currency’s minor units.
pub fn parse(text: String) -> Result(Money, MoneyError)
Reads the form to_string writes. Zeros are added up to the currency’s
minor units, so BRL 5 is BRL 5.00; a nonzero digit beyond them is
Arithmetic(WouldRound).
pub fn split(
money: Money,
into parts: Int,
) -> Result(List(Money), MoneyError)
Splits the amount into parts equal shares, in minor units.
pub fn sum(
amounts: List(Money),
in currency: currency.Currency,
) -> Result(Money, MoneyError)
The total of amounts in currency; sum([], in: currency) is zero.
pub fn to_minor_units(money: Money) -> Result(Int, MoneyError)
The amount counted in minor units: BRL 10.50 is 1050.
pub fn to_string(money: Money) -> String
The canonical form for logs and audit trails: the code, one space and
the amount, as in BRL 1234.50. For display, format amount with
glecimal/notation.
pub fn zero(in currency: currency.Currency) -> Money
Zero in currency, at its scale: zero(in: currency.brl) is BRL 0.00.