Is it possible to create commutative generics, suc...
# announcements
n
Is it possible to create commutative generics, such that Foo<A, B> is the same as Foo<B, A>?
r
No, and for the life of me I can't think of a use case for that. Care to elaborate?
n
Algebraic expressions for things like units. Think N*m == m*N. Or unions where Union<A,B> == Union<B,A>.
d
Maybe it should be Foo<A, A> then 🙂
👆 2
s
Arrow has support coming for Union types: https://github.com/arrow-kt/arrow-meta/pull/64
r
I still don't see how "commutative generics" would help with algebra.
N
and
m
would still need to be associated with either
A
or
B
.
n
N and m here were references to units. A torque for example, is a magnitude and the units Force (say Newtons) and Length (say meters): 10 Nm. But it is also valid to have 10 mN. This order agnosticism allows torque to be created by multiplying a force and length in a commutative way. Generics seem like a potential path to allowing this. But there might be a better language construct for this eventually. Unions for example (ala Typescript or Ceylon) are order agnostic I believe. Unions don’t solve the unit issue of course since there you really need an unordered list of types.
n
I think I understand the sentiment. But the same argument could be made for (most) method parameters: having a
fun foo(a: Int, b: String)
, one could argue that I should be allowed to invoke that with
foo("hello", 42)
. And the language actually would support that by using
foo(b = "Hello", a = 42)
. So, are you looking for something similar but for type parameters?
s
Like @nkiesel said, you could create functions that allow that flexibility while the type still has a strict order to its type params, something like this:
Copy code
interface Unit {
    val value: Double
}

interface Magnitude<U1: Unit, U2: Unit> {
    val value: Double
}

interface MagnitudeFactory<U1: Unit, U2: Unit, M: Magnitude<U1, U2>> {
    operator fun invoke(value1: Unit, value2: Unit): M
}

/* ====== */

class Force(override val value: Double): Unit

class Length(override val value: Double): Unit

class Torque(override val value: Double): Magnitude<Force, Length> {

    companion object: MagnitudeFactory<Force, Length, Torque> {
        override operator fun invoke(value1: Unit, value2: Unit) =
            Torque(value1.value * value2.value)
    }
}

val Double.Nm: Torque get() = Torque(this)

val <http://Double.mN|Double.mN>: Torque get() = Torque(this)

operator fun Force.times(length: Length): Torque = Torque(this, length)

operator fun Length.times(force: Force): Torque = Torque(this, force)

/* ====== */

fun test() {
    val t1 = 10.4.Nm
    val t2 = 23.4.mN
    val t3 = Force(3.1) * Length(4.0)
    val t4 = Length(4.0) * Force(3.1)
}
👍 1