alchemist
Alchemist allows you to manage physical quanities defined in the International System of Units. Like Duration, alchemist models its quanities as a value class with a single underlying Long
value.
Type Safety
Alchemist's main goal is to provide type safety for arithmetic on physical quanities. There's no need to pass loosely typed Long
, Int
, or Double
values around anymore!
val energy1 = 10.wattHours
val energy2 = 10.millijoules
println(energy1 + energy2) // OK: both are type Energy.
val power = 10.watts
println(energy1 + power) // Compiler Error!
Scalar Arithmetic
Physical quantities expose scalar arithmetic like the following:
val power = 10.watts
println(power / 2) // "5W"
println(power * 100) // "1kW"
println(-power) // "-10W"
and each quantity exposes typed arithmetic:
val first = 10.wattHours
val second = 2.wattHours
println(first + second) // "12Wh"
println(first - second) // "8Wh"
println(second - first) // "-8Wh"
println(first / second) // "5.0"
Physical Arithmetic
Physical quantities expose valid physical arithmetic defined in the International System of Units. For example:
val energy = 10.wattHours
val power = 10.watts
val duration: Duration = energy / power
println(duration) // "1h"
val length = 10.nanometers
val force: Force = energy / length
println(force) // "3.6TN"
val velocity = length / 1.seconds
println(velocity) // 10 nm/s
val acceleration = velocity / 1.seconds
println(acceleration) // 10 nm/s²
Custom Quantity Unit Implementation
Every unit alchemist exposes is an interface, thus allowing you to implement non-standard or uncommonly used units:
// Alchemist does not provide horsepower out of the box.
object HorsePower : PowerUnit {
override val symbol: String get() = "hp"
override val microwattScale: Long get() = 745_699_871
}
val Int.horsepower: Power get() = this.toPower(HorsePower)
println(1.horsepower) // "745.7W"
Note that alchemist does not provide certain units out of the box because they're impossible to represent without losing precision. In the above example, 1 horsepower is actually 745,699,871.58μW.
Infinity
While Long values can store huge numbers, sometimes they're not enough for the operations you're attempting to perform. Rather than silently over or underflowing during an arithmetic operation, Alchemist will clamp your quantities to a special infinite value. These special infinite values have some restrictions.
// This would overflow.
val infiniteEnergy = 100.joules * Long.MAX_VALUE
println(infiniteEnergy) // "Infinity"
// Infinite values don't behave like other values.
println(infiniteEnergy / Long.MAX_VALUE) // "Infinity"
println(infiniteEnergy * 10) // "Infinity"
println(-infiniteEnergy) // "-Infinity"
// Some operations are invalid with infinite values.
println(infiniteEnergy / infiniteEnergy) // Oops! This throws IllegalArgumentException.
println(infiniteEnergy * -infiniteEnergy) // This throws, too.