Skip to content

Instantly share code, notes, and snippets.

@rabidaudio
Created July 12, 2017 02:12
Show Gist options
  • Save rabidaudio/7f8d40e7c44cad44a2748f34c7a43e4e to your computer and use it in GitHub Desktop.
Save rabidaudio/7f8d40e7c44cad44a2748f34c7a43e4e to your computer and use it in GitHub Desktop.
Playing with unit analysis in kotlin
// see https://discuss.kotlinlang.org/t/units-of-measure/3454/7
// see http://javanut.net/2017/05/23/more-fun-with-generics-in-kotlin/
//class Bag<T> private constructor(val wrapped: List<T>): Collection<T> by wrapped {
//
// private class DefaultComparator<T>: Comparator<T> {
// override fun compare(o1: T, o2: T): Int = o1.toString().compareTo(o2.toString())
// }
//
// companion object {
// fun <T> create(items: Collection<T>, comparator: Comparator<T> = DefaultComparator()) = Bag(items.sortedWith(comparator))
// }
//
// fun plus(other: T): Bag<T> = Bag(wrapped.plus(other))
//}
//
//fun <T> bagOf(vararg items: T): Bag<T> = Bag.create(items.asList())
///////////
interface UnitType {
val baseUnit: Unit
}
interface Unit {
// operator fun times(other: Unit): CombinedUnit = CombinedUnit(
// numerator = bagOf(this, other),
// denominator = bagOf()
// )
//
// operator fun div(other: Unit): CombinedUnit = CombinedUnit(
// numerator = bagOf(this),
// denominator = bagOf(other)
// )
//
val type: UnitType
operator fun times(other: Unit): Unit = when (other) {
is CombinedUnit -> other.times(this)
this -> CombinedUnit(mapOf(this to 2))
else -> CombinedUnit(mapOf(this to 1, other to 1))
}
operator fun div(other: Unit): Unit = when (other) {
is CombinedUnit -> other.div(this)
this -> CombinedUnit(emptyMap()) // unit-less
else -> CombinedUnit(mapOf(this to 1, other to -1))
}
}
private data class CombinedUnit(val units: Map<Unit, Int>): Unit {
override operator fun times(other: Unit): Unit = when (other) {
is CombinedUnit -> CombinedUnit(
(units.keys + other.units.keys).map { u -> Pair(u, (units[u] ?: 0) + (other.units[u] ?: 0)) }.toMap()
)
else -> copySettingPower(other, (units[other] ?: 0) + 1)
}
override operator fun div(other: Unit): Unit = when(other) {
is CombinedUnit -> CombinedUnit(
(units.keys + other.units.keys).map { u -> Pair(u, (units[u] ?: 0) - (other.units[u] ?: 0)) }.toMap()
)
else -> copySettingPower(other, (units[other] ?: 0) - 1)
}
private fun copySettingPower(unit: Unit, power: Int): CombinedUnit = CombinedUnit(
units = if (power == 0) units.minus(unit) else units.plus(Pair(unit, power))
)
override fun toString(): String = units.map { "${it.key}^${it.value}" }.joinToString("")
}
object Distance: UnitType {
override val baseUnit = Meters
}
object Time: UnitType {
override val baseUnit = Seconds
}
object Meters: Unit
object Seconds: Unit
data class Value(val value: Number, val unit: Unit)
infix fun Number.of(unit: Unit) = Value(this, unit)
val m = 20 of Meters
val s = 5 of Seconds
val mps = Meters / Seconds
val mps2 = mps / Seconds
object MPS = Meters / Seconds
val speed = Value(20 / 5, Meters / Seconds)
@rabidaudio
Copy link
Author

more verbose

inline class Seconds(val value: Double)
inline class Meters(val value: Double)
inline class MetersPerSecond(val value: Double)
inline class MetersPerSecondSquared(val value: Double)

operator fun Meters.div(seconds: Seconds) = MetersPerSecond(this.value / seconds.value)
operator fun MetersPerSecond.div(seconds: Seconds) = MetersPerSecondSquared(this.value / seconds.value)
operator fun MetersPerSecondSquared.times(seconds: Seconds) = MetersPerSecond(this.value * seconds.value)
operator fun MetersPerSecond.times(seconds: Seconds) = Meters(this.value * seconds.value)

operator fun Seconds.plus(other: Seconds) = Seconds(this.value + other.value)
operator fun Seconds.minus(other: Seconds) = Seconds(this.value - other.value)
operator fun Meters.plus(other: Meters) = Meters(this.value + other.value)
operator fun Meters.minus(other: Meters) = Meters(this.value - other.value)

inline infix fun <T> Double.of(ctor: (Double)->T): T = ctor.invoke(this)

fun main() {
    val s = 10.0 of ::Seconds
    val m = 100.0 of ::Meters

    val v = m / s
    val a = m / s / s

    val q = a * s
    val r = (m + m) / (s + s)
}

@rabidaudio
Copy link
Author

even better is this, which is generated: https://github.com/kunalsheth/units-of-measure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment