TMoney is a thin value-type wrapper layering an ISO-4217 currency tag onto
an exact TDecimal amount. The numeric core (TDecimal) stays currency- and
locale-agnostic; currency policy lives entirely in this wrapper, matching
Moneta / money-gem / rusty-money.
Design:
- Currency is an upper-cased ISO-4217 string code; the set is open (unknown
codes are accepted at the fallback minor-unit scale of 2).
- A built-in registry gives each currency its default scale (JPY 0, USD 2,
KWD 3, fallback 2); every TMoney is normalised to its currency's scale on
construction and after every operation, using banker's rounding.
- Cross-currency Add/Subtract/Compare raise EMoneyMismatch (no implicit
conversion); Equals is total (False, not raise, across currencies).
- Immutable value semantics, mirroring TDecimal.
API: free-function constructors (MoneyFromStr / MoneyFromDecimal /
MoneyFromInt / MoneyZero), record methods (Amount, CurrencyCode, IsZero,
Sign, Add, Subtract, Negate, Multiply, MultiplyInt, Compare, Equals,
AmountString, ToString), and the CurrencyScale registry function.
Tests:
- cp.test.numerics.money.pas: 22 IR/semantic tests (resolution, IR shape,
type errors) via TUnitLoader.
- cp.test.e2e.numerics.money.pas: 24 dual-backend e2e tests
(CompileAndRunWithRTL) covering construction + per-currency normalisation,
banker's rounding, case-folding, arithmetic, mismatch raising,
Compare/Equals/IsZero/Sign, the registry, and a realistic invoice flow.
Docs: language-rationale.adoc gains "Currency Amounts — TMoney Wraps
TDecimal, Currency Is a String Tag" (decision + alternatives);
future-improvements.adoc marks Numerics.Money as implemented.
Verified: FIXPOINT_OK, NATIVE_FIXPOINT_OK, NATIVE_INTERNAL_OK; full suite
green on both the QBE-built and native-built runners (3671 tests).