Interface that unites all base number types and their common methods/operators

I’m making a value class that I want to use to represent any number, integer or floating, in a particular unit. I want to make methods/operators on that class in the following way :

fun Metre.unaryPlus() = +value

The issue is that Number (in addition to being an abstract class instead of an interface for some reason) does not support unaryPlus or any other operator, despite all of its concrete implementations as the basic types, actually supporting them.

How can I fill the T in the following:

value class Second(public val value: T)

such that I may call unaryPlus, unaryMinus, plus, minus, times, div, rem, equals and compareTo on it (or between it and another T) ?

I would put Byte|Short|Int|Long|Float|Double but type unions aren’t a thing (is it allowed to cheer for Typescript here ?).

Maybe self-types could help?

interface Metre<Self: Metre<Self, T>, T: Number> {
    val value: T
    operator fun plus(other: Self): Self
}

@JvmInline
value class MetreInt(override val value: Int) : Metre<MetreInt, Int> {
    override fun plus(other: MetreInt): MetreInt = MetreInt(this.value + other.value)
}

@JvmInline
value class MetreDouble(override val value: Double) : Metre<MetreDouble, Double> {
    override fun plus(other: MetreDouble): MetreDouble = MetreDouble(this.value + value)
}
...

fun Metre(value: Int) = MetreInt(value)
fun Metre(value: Double) = MetreDouble(value)
...

Of course this gets annoying quickly, but then you could try code generation.

1 Like

Note that you can’t do this for every concrete Number type, because there is an infinite number of possible classes that extend Number. For example, in addition to the primitive wrappers (Int, Double etc), there is BigDecimal and third-party libraries offering Rational etc.

1 Like

That’s true, as you cannot anticipate how some custom Number type does addition etc. The Number interface is really pathetic in this regard, but there is no good solution for this, except if the Kotlin team decides to retrofit some richer interfaces into the existing hierarchy (similar to bringing MutableList etc into the collection mix).

If the operations are used in a lot of places, and if you want to make it easy to support custom types, the best you could do is to add a typeclass-like interface for arithmetic, e.g.

interface Arithmetic<T: Number> {
    val zero: T
    val one: T
    operator fun T.plus(other: T): T
    operator fun T.minus(other: T): T
    operator fun T.times(other: T): T
}

Right now, this would be still cumbersome, but maybe this would be a good candidate for a context parameter (but I don’t know how well they work with generics).

1 Like

It is not possible to abstract away number operations. What is the type of sum of Int and BigDecimal? The design of Number class was a mistake in Java. It should not be open for inheritance.

If you are talking about units, it could be done. There are several implementations like GitHub - nacular/measured: Intuitive, type-safe units of measure or more specialized libraries.

2 Likes