Created
July 12, 2017 02:12
-
-
Save rabidaudio/7f8d40e7c44cad44a2748f34c7a43e4e to your computer and use it in GitHub Desktop.
Playing with unit analysis in kotlin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
more verbose