
Facilitates shared data types across projects, addressing duplication issues. Includes types for Location, Locale, Currency, and potentially Duration shortcuts for types like Distance.
A Kotlin Multiplatform library of strongly-typed, shareable data types — money and currency, locales, coordinates and telemetry, GeoJSON geometry, RFC-compliant identifiers, and arbitrary-precision math. It exists because KMP projects kept redefining the same types incompatibly; these are the shared ones, with no platform-specific API leaking through.
Targets: Android, JVM, iOS, JS, Wasm and Linux.
Published to Maven Central as io.github.aughtone:types.
// build.gradle.kts
implementation("io.github.aughtone:types:4.0.0")Or through a version catalog:
# gradle/libs.versions.toml
[versions]
aughtone-types = "4.0.0"
[libraries]
aughtone-types = { module = "io.github.aughtone:types", version.ref = "aughtone-types" }// build.gradle.kts
implementation(libs.aughtone.types)[!IMPORTANT] v4.0.0 Breaking Changes: A breaking release.
Outcome.ErrorandLocale.toLanguageTag()are removed;Moneyequality is now numeric so5.1equals5.10;DistanceandSpeedthrow instead of clamping to zero;GeoBoundingBoxleaves the geometry hierarchy;GeoFeature.propertiesbecomesJsonObject; invalid GeoJSON is now rejected; andUnitOfMeasure.findFirstrefuses ambiguous symbols rather than guessing. Several change behaviour without a compile error — see the changelog before upgrading.v3.4.0
Outcome.Errorrenamed: The failure case ofOutcomeis nowOutcome.Failure, and the factory isOutcome.failure(...). The old names shipped as deprecated aliases in 3.4.0 and are removed in 4.0.0.Outcome$Errorno longer exists as a class, so upgrading from 3.3.0 needs a clean and rebuild rather than a code change.v3.3.0 Locale Display Names: Eleven locale
displayNamevalues are corrected — renamed countries (Czechia,North Macedonia,Türkiye), incomplete or abbreviated country names, and dated language exonyms (Farsi→Persian,Azeri→Azerbaijani). No API changed, but snapshot tests and cached UI strings holding the old names will need updating. See the changelog for the full table.v3.2.0 Behavioral Fixes: Still worth reading if you are coming from 3.1.x — that release corrected long-standing bugs whose output or validation changes for existing code.
UrlEncoder.encodeis now true RFC 3986 percent-encoding — useencodeFormDatafor the previousapplication/x-www-form-urlencodedbehavior.Url/Uri/Urn/GeoUristring output is now well-formed, andUrn/GeoUriconstruction now rejects invalid input. Critical arbitrary-precision fixes also land inBigInteger/BigDecimaldivision andBankersValue. See the changelog for the full list.v3.0.0 Breaking Change: All GeoJSON geometry types (e.g.,
Point,Polygon) have been renamed with aGeoprefix (e.g.,GeoPoint,GeoPolygon).Moneynow usesBigDecimalfor its internal value to support sub-minor units, andTelemetryhas moved to thequantitativepackage.
docs/knowledge/, work in Issues.| Category | Type | Standard / Compliance | Description |
|---|---|---|---|
| Financial | Money |
Banker's Rounding | Arbitrary-precision monetary values with BigDecimal storage. |
Currency |
ISO 4217 | Global currency definitions with scale factors. | |
| Localization | Locale |
BCP 47 | Universal language, region, and script identifiers. |
| Quantitative | Coordinates |
WGS84 | Geodetic latitude and longitude degrees. |
Distance |
SI (Meters) | Linear distance with accuracy support. | |
Speed |
SI (mps) | Rate of motion in meters per second. | |
Altitude |
SI (Meters) | Vertical distance above/below reference. | |
Azimuth |
Degrees | Compass bearing (0-360°). | |
Telemetry |
Unified Domain | Comprehensive model with coordinates, azimuth, speed, and altitude. | |
| Geospatial | GeoJson |
RFC 7946 |
GeoPoint, GeoFeature, and GeoFeatureCollection models. |
| SI Units | UnitOfMeasure |
SI / Imperial | Definitions for meters, liters, bytes, etc. |
MetricPrefix |
SI Prefixes | Scaling factors from Quetta to Quecto. |
|
| Identifiers | Url |
RFC 3986 | Uniform Resource Locators (Web). |
Urn |
RFC 8141 | Uniform Resource Names (Persistent IDs). | |
GeoUri |
RFC 5870 | Geographic 'geo' URI scheme. | |
| Mathematics | BigInteger |
Pure Kotlin | Arbitrary-precision integer math support. |
BigDecimal |
Pure Kotlin | Arbitrary-precision decimal math with rounding support. | |
| Utilities | BitSet |
Multiplatform | Space-efficient storage for bit-level flags. |
BankersValue |
Half-to-Even | Precision math with bias-free rounding rules. | |
| Control Flow | Outcome |
Sealed (KMP-safe) | Success-or-failure result that survives the Swift/JS boundary, unlike kotlin.Result. |
Locale.current or localeFor("fr-CH").Money(12.50, Currency.Usd) or Money(BigDecimal("1.23456"), Currency.Eur).Telemetry(coords, speed = 2.5.mps, azimuth = 90.degrees).100.meters or 5.kilometers.UnitOfMeasure.Litre.symbol ("L"), MetricPrefix.Kilo.Url("https://pkg.dev"), Urn("urn:uuid:...").GeoUri(45.5, -122.6) (RFC 5870).BigInteger("999999999999999999999999") or BigDecimal("123.456").BigDecimal("1.255").setScale(2, RoundingMode.HALF_EVEN) -> 1.26.runOutcome { parse(input) } returns Outcome.Success or Outcome.Failure; when over the two, or use fold, map, recover, dataOrElse.kotlin.Result it is a sealed class, so Swift and JavaScript callers can read the failure as data. See ADR-0004.GeoPoint(45.5, -122.6, 100.0).toGeoJson() (RFC 7946).Locale.displayName from the bundled resource data is always English. To show a locale's name in the end user's own language:
localeFor("bn")?.localizedDisplayName() // "bengali" for a French user, "ベンガル語" for a Japanese userRather than bundling a full translation matrix (~90 × 90 names) into every app, this delegates to the CLDR data each platform already ships — java.util.Locale (JVM/Android), NSLocale (Apple), Intl.DisplayNames (JS/Wasm). Bundled resource files aren't viable everywhere: browsers can't read files synchronously, and klibs can't deliver resources into an iOS app bundle.
Tradeoffs to be aware of:
Intl.DisplayNames (widely available since ~2020); older environments fall back to English.null — the worst case is the English displayName.See ADR-0003 for the full rationale, and issue #20 for the deferred bundled-tables alternative.
To ensure mathematical precision and behavior consistency, this library employs rigorous Differential Parity Testing against standard baseline libraries:
java.math.BigInteger and java.math.BigDecimal).BigInteger and BigDecimal are benchmarked against the JDK types. Figures are relative to java.math, so 1.0x is parity and below 1.0x is faster than the JDK.
| Values of 40 digits or more | vs java.math
|
|---|---|
| Division, remainder, modulo | 0.5x – 0.8x |
| Bitwise operations | 0.5x – 0.8x |
Text conversion (toString, parsing) |
0.8x – 3.4x |
Scaling, rounding, BigDecimal division |
0.9x – 1.2x |
| Add, subtract, multiply, shifts | 1.2x – 2.9x |
Small values are the exception, and deliberately so: the JDK keeps any BigDecimal under 19 digits in a long, so it answers toDouble on one by reading a field. There is no equivalent fast path here, and short-value operations run from several times to two orders of magnitude slower as a result. Two other costs are worth knowing: modPow is around 5x, lacking Montgomery reduction, and comparing two BigDecimal values of different scale is quick only when their magnitudes differ enough to settle it without aligning them — equal scales, the common case, beat the JDK.
Run them yourself with ./gradlew :benchmarks:benchmark. Ratios hold reasonably across machines; absolute throughput does not, so the benchmarks report both.
A Kotlin Multiplatform library of strongly-typed, shareable data types — money and currency, locales, coordinates and telemetry, GeoJSON geometry, RFC-compliant identifiers, and arbitrary-precision math. It exists because KMP projects kept redefining the same types incompatibly; these are the shared ones, with no platform-specific API leaking through.
Targets: Android, JVM, iOS, JS, Wasm and Linux.
Published to Maven Central as io.github.aughtone:types.
// build.gradle.kts
implementation("io.github.aughtone:types:4.0.0")Or through a version catalog:
# gradle/libs.versions.toml
[versions]
aughtone-types = "4.0.0"
[libraries]
aughtone-types = { module = "io.github.aughtone:types", version.ref = "aughtone-types" }// build.gradle.kts
implementation(libs.aughtone.types)[!IMPORTANT] v4.0.0 Breaking Changes: A breaking release.
Outcome.ErrorandLocale.toLanguageTag()are removed;Moneyequality is now numeric so5.1equals5.10;DistanceandSpeedthrow instead of clamping to zero;GeoBoundingBoxleaves the geometry hierarchy;GeoFeature.propertiesbecomesJsonObject; invalid GeoJSON is now rejected; andUnitOfMeasure.findFirstrefuses ambiguous symbols rather than guessing. Several change behaviour without a compile error — see the changelog before upgrading.v3.4.0
Outcome.Errorrenamed: The failure case ofOutcomeis nowOutcome.Failure, and the factory isOutcome.failure(...). The old names shipped as deprecated aliases in 3.4.0 and are removed in 4.0.0.Outcome$Errorno longer exists as a class, so upgrading from 3.3.0 needs a clean and rebuild rather than a code change.v3.3.0 Locale Display Names: Eleven locale
displayNamevalues are corrected — renamed countries (Czechia,North Macedonia,Türkiye), incomplete or abbreviated country names, and dated language exonyms (Farsi→Persian,Azeri→Azerbaijani). No API changed, but snapshot tests and cached UI strings holding the old names will need updating. See the changelog for the full table.v3.2.0 Behavioral Fixes: Still worth reading if you are coming from 3.1.x — that release corrected long-standing bugs whose output or validation changes for existing code.
UrlEncoder.encodeis now true RFC 3986 percent-encoding — useencodeFormDatafor the previousapplication/x-www-form-urlencodedbehavior.Url/Uri/Urn/GeoUristring output is now well-formed, andUrn/GeoUriconstruction now rejects invalid input. Critical arbitrary-precision fixes also land inBigInteger/BigDecimaldivision andBankersValue. See the changelog for the full list.v3.0.0 Breaking Change: All GeoJSON geometry types (e.g.,
Point,Polygon) have been renamed with aGeoprefix (e.g.,GeoPoint,GeoPolygon).Moneynow usesBigDecimalfor its internal value to support sub-minor units, andTelemetryhas moved to thequantitativepackage.
docs/knowledge/, work in Issues.| Category | Type | Standard / Compliance | Description |
|---|---|---|---|
| Financial | Money |
Banker's Rounding | Arbitrary-precision monetary values with BigDecimal storage. |
Currency |
ISO 4217 | Global currency definitions with scale factors. | |
| Localization | Locale |
BCP 47 | Universal language, region, and script identifiers. |
| Quantitative | Coordinates |
WGS84 | Geodetic latitude and longitude degrees. |
Distance |
SI (Meters) | Linear distance with accuracy support. | |
Speed |
SI (mps) | Rate of motion in meters per second. | |
Altitude |
SI (Meters) | Vertical distance above/below reference. | |
Azimuth |
Degrees | Compass bearing (0-360°). | |
Telemetry |
Unified Domain | Comprehensive model with coordinates, azimuth, speed, and altitude. | |
| Geospatial | GeoJson |
RFC 7946 |
GeoPoint, GeoFeature, and GeoFeatureCollection models. |
| SI Units | UnitOfMeasure |
SI / Imperial | Definitions for meters, liters, bytes, etc. |
MetricPrefix |
SI Prefixes | Scaling factors from Quetta to Quecto. |
|
| Identifiers | Url |
RFC 3986 | Uniform Resource Locators (Web). |
Urn |
RFC 8141 | Uniform Resource Names (Persistent IDs). | |
GeoUri |
RFC 5870 | Geographic 'geo' URI scheme. | |
| Mathematics | BigInteger |
Pure Kotlin | Arbitrary-precision integer math support. |
BigDecimal |
Pure Kotlin | Arbitrary-precision decimal math with rounding support. | |
| Utilities | BitSet |
Multiplatform | Space-efficient storage for bit-level flags. |
BankersValue |
Half-to-Even | Precision math with bias-free rounding rules. | |
| Control Flow | Outcome |
Sealed (KMP-safe) | Success-or-failure result that survives the Swift/JS boundary, unlike kotlin.Result. |
Locale.current or localeFor("fr-CH").Money(12.50, Currency.Usd) or Money(BigDecimal("1.23456"), Currency.Eur).Telemetry(coords, speed = 2.5.mps, azimuth = 90.degrees).100.meters or 5.kilometers.UnitOfMeasure.Litre.symbol ("L"), MetricPrefix.Kilo.Url("https://pkg.dev"), Urn("urn:uuid:...").GeoUri(45.5, -122.6) (RFC 5870).BigInteger("999999999999999999999999") or BigDecimal("123.456").BigDecimal("1.255").setScale(2, RoundingMode.HALF_EVEN) -> 1.26.runOutcome { parse(input) } returns Outcome.Success or Outcome.Failure; when over the two, or use fold, map, recover, dataOrElse.kotlin.Result it is a sealed class, so Swift and JavaScript callers can read the failure as data. See ADR-0004.GeoPoint(45.5, -122.6, 100.0).toGeoJson() (RFC 7946).Locale.displayName from the bundled resource data is always English. To show a locale's name in the end user's own language:
localeFor("bn")?.localizedDisplayName() // "bengali" for a French user, "ベンガル語" for a Japanese userRather than bundling a full translation matrix (~90 × 90 names) into every app, this delegates to the CLDR data each platform already ships — java.util.Locale (JVM/Android), NSLocale (Apple), Intl.DisplayNames (JS/Wasm). Bundled resource files aren't viable everywhere: browsers can't read files synchronously, and klibs can't deliver resources into an iOS app bundle.
Tradeoffs to be aware of:
Intl.DisplayNames (widely available since ~2020); older environments fall back to English.null — the worst case is the English displayName.See ADR-0003 for the full rationale, and issue #20 for the deferred bundled-tables alternative.
To ensure mathematical precision and behavior consistency, this library employs rigorous Differential Parity Testing against standard baseline libraries:
java.math.BigInteger and java.math.BigDecimal).BigInteger and BigDecimal are benchmarked against the JDK types. Figures are relative to java.math, so 1.0x is parity and below 1.0x is faster than the JDK.
| Values of 40 digits or more | vs java.math
|
|---|---|
| Division, remainder, modulo | 0.5x – 0.8x |
| Bitwise operations | 0.5x – 0.8x |
Text conversion (toString, parsing) |
0.8x – 3.4x |
Scaling, rounding, BigDecimal division |
0.9x – 1.2x |
| Add, subtract, multiply, shifts | 1.2x – 2.9x |
Small values are the exception, and deliberately so: the JDK keeps any BigDecimal under 19 digits in a long, so it answers toDouble on one by reading a field. There is no equivalent fast path here, and short-value operations run from several times to two orders of magnitude slower as a result. Two other costs are worth knowing: modPow is around 5x, lacking Montgomery reduction, and comparing two BigDecimal values of different scale is quick only when their magnitudes differ enough to settle it without aligning them — equal scales, the common case, beat the JDK.
Run them yourself with ./gradlew :benchmarks:benchmark. Ratios hold reasonably across machines; absolute throughput does not, so the benchmarks report both.