How do I convert currencies and format money for display in EK9?

← Getting Started · Ref: Q150

EK9 provides currency conversion, locale-aware formatting, and stream collection for Money values.

CURRENCY CONVERSION

Two overloads for converting between currencies:

  inUSD <- amount.convert(1.32845, "USD")    Rate + target currency string
  inUSD <- amount.convert(1.32845#USD)        Rate embedded in money literal

Both multiply the amount by the rate and return a new Money value in the target currency, rounded to that currency's precision.

LOCALE FORMATTING

Four levels of locale-aware formatting using the Locale type:

  locale.format(money)         Full: currency symbol + amount + fractions
  locale.longFormat(money)     No symbol: amount + fractions
  locale.mediumFormat(money)   No fractions: currency symbol + amount
  locale.shortFormat(money)    Neither: amount only

The same money value formats differently in different locales:

  en_GB: GBP 10.00    de_DE: 10,00 GBP

STREAM COLLECTION

Collect a stream of money values into a total:

  amounts as List of Money := [10#GBP, 89.51#GBP, 49.76#GBP]
  total <- cat amounts | collect as Money

The collect operation adds all values in the stream. All values must be the same currency — mixed currencies produce an unset result.

See Q35 for Money arithmetic and literals. See Q149 for currency safety and why Money is built-in. See Q44 for the complete Locale type API. See Q89 for stream pipeline basics.

Example

defines module qa.money.formatting

  defines program
    MoneyFormattingDemo()
      stdout <- Stdout()

      tenPounds <- 10#GBP
      amounts as List of Money := [tenPounds, 89.51#GBP, 49.76#GBP]

      // === STREAM COLLECTION ===

      streamTotal <- cat amounts | collect as Money
      require streamTotal == 159.27#GBP
      stdout.println(`Stream total: ${streamTotal}`)

      // === CURRENCY CONVERSION ===

      // Convert with exchange rate and target currency
      totalInUSD <- streamTotal.convert(1.32845, "USD")
      require totalInUSD == 211.58#USD
      stdout.println(`Converted: ${totalInUSD}`)

      // Alternative syntax: convert with Money literal
      alsoUSD <- streamTotal.convert(1.32845#USD)
      require alsoUSD == 211.58#USD

      // === LOCALE FORMATTING ===

      enGB <- Locale("en_GB")
      deutsch <- Locale("de_DE")

      // Four format levels
      stdout.println(`Full: ${enGB.format(tenPounds)}`)
      stdout.println(`No symbol: ${enGB.longFormat(tenPounds)}`)
      stdout.println(`No fractions: ${enGB.mediumFormat(tenPounds)}`)
      stdout.println(`Neither: ${enGB.shortFormat(tenPounds)}`)

      // Same currency, different locale
      stdout.println(`German: ${deutsch.format(tenPounds)}`)

Common mistakes

E50060 — Formatting is done via the Locale type, not a method on Money. Create a Locale then call locale.format(money). Four levels available: format, longFormat, mediumFormat, shortFormat. See ek9 -h E50060 for details.

Incorrect:

stdout.println(`Full: ${tenPounds.format(enGB)}`)

Correct:

stdout.println(`Full: ${enGB.format(tenPounds)}`)

E50060 — EK9 has no toString() method. Use string interpolation or the $ prefix operator for string conversion. See ek9 -h E50060 for details.

Incorrect:

stdout.println(streamTotal.toString())

Correct:

stdout.println(`Stream total: ${streamTotal}`)

E50060 — Currency conversion requires an exchange rate. The convert method takes either (Float, String) or (Money) — the rate is always required. There is no automatic rate lookup. See ek9 -h E50060 for details.

Incorrect:

totalInUSD <- streamTotal.convert("USD")

Correct:

totalInUSD <- streamTotal.convert(1.32845, "USD")
Other ways to ask this
  • How do I convert between currencies in EK9?
  • How do I format money with locale in EK9?
  • How do I sum a list of money values in EK9?
  • How does locale affect money display in EK9?

Coming from another language?

Java: BigDecimal with explicit RoundingMode, NumberFormat for locale, Currency class separate from amount. Python: decimal.Decimal + locale module, babel for formatting. JavaScript: Intl.NumberFormat for locale, no built-in currency conversion. Rust: no built-in, external crates for both. Go: no built-in, golang.org/x/text for formatting. EK9: built-in convert() method with two overloads, four locale formatting levels, stream collection with cat|collect as Money.

Keywords: intro, collect, formatting, exchange, stream, start, display, rate, money, locale, format, conversion, convert, beginner, currency, first