Kotlin quadruple, quintuple, etc. for destructuring

Viewed 21789

I am looking for a clean way to create destructurable objects in-line. kotlin.Pair and kotlin.Triple cover a lot of use cases, but sometimes there are more objects that are needed to be passed.

One sample use case is RX's zip function, where the results of several I/O calls need to be mapped into another object:

Single
    .zip(repositoryA.loadData(someId),
         repositoryB.loadData(someId),
         repositoryC.loadAll(),
         repositoryD.loadAll()),
         { objectA, objectB, objectsC, objectsD -> /*some Kotlin magic*/ }
    )
    .map { (objectA, objectB, objectsC, objectsD) -> /*do the mapping*/ }

I am trying to figure out what would go in the "some Kotlin magic" part. If there were only 3 repositories, it would be

Triple(objectA, objectB, objectsC)

Do I need to create a new data class for this, and for any n-tuple case, or is there another way?

4 Answers

I found it easiest to just code-gen the n-tuples needed. Use-case was functional extension methods for things like memoization, etc.

data class NTuple2<T1, T2>(val t1: T1, val t2: T2)

data class NTuple3<T1, T2, T3>(val t1: T1, val t2: T2, val t3: T3)

data class NTuple4<T1, T2, T3, T4>(val t1: T1, val t2: T2, val t3: T3, val t4: T4)

data class NTuple5<T1, T2, T3, T4, T5>(val t1: T1, val t2: T2, val t3: T3, val t4: T4, val t5: T5)

data class NTuple6<T1, T2, T3, T4, T5, T6>(val t1: T1, val t2: T2, val t3: T3, val t4: T4, val t5: T5, val t6: T6)

And then generate the necesssary construction-helpers:

infix fun <T1, T2> T1.then(t2: T2): NTuple2<T1, T2>
{
    return NTuple2(this, t2)
}

infix fun <T1, T2, T3> NTuple2<T1, T2>.then(t3: T3): NTuple3<T1, T2, T3>
{
    return NTuple3(this.t1, this.t2, t3)
}

infix fun <T1, T2, T3, T4> NTuple3<T1, T2, T3>.then(t4: T4): NTuple4<T1, T2, T3, T4>
{
    return NTuple4(this.t1, this.t2, this.t3, t4)
}

infix fun <T1, T2, T3, T4, T5> NTuple4<T1, T2, T3, T4>.then(t5: T5): NTuple5<T1, T2, T3, T4, T5>
{
    return NTuple5(this.t1, this.t2, this.t3, this.t4, t5)
}

infix fun <T1, T2, T3, T4, T5, T6> NTuple5<T1, T2, T3, T4, T5>.then(t6: T6): NTuple6<T1, T2, T3, T4, T5, T6>
{
    return NTuple6(this.t1, this.t2, this.t3, this.t4, this.t5, t6)
}

So I could then do:

val nTuple4 = 1 then 2 then "foo" then "bar"

Resulting in:

val nTuple4: NTuple4<Int, Int, String, String>

You can add these kotlin classes to your project, they will work exactly like Pair or Triple but you will be able to pass more objects.

Quadruple (lets you pass 4 objects)

import java.io.Serializable

/**
 * Created by nalcalag on 09/02/2019.
 * 
 * Represents a quartet of values
 *
 * There is no meaning attached to values in this class, it can be used for any purpose.
 * Quadruple exhibits value semantics
 *
 * @param A type of the first value.
 * @param B type of the second value.
 * @param C type of the third value.
 * @param D type of the fourth value.
 * @property first First value.
 * @property second Second value.
 * @property third Third value.
 * @property fourth Fourth value.
 */
data class Quadruple<out A, out B, out C, out D>(
        val first: A,
        val second: B,
        val third: C,
        val fourth: D
) : Serializable {

    /**
     * Returns string representation of the [Quadruple] including its [first], [second], [third] and [fourth] values.
     */
    override fun toString(): String = "($first, $second, $third, $fourth)"
}

/**
 * Converts this quadruple into a list.
 */
fun <T> Quadruple<T, T, T, T>.toList(): List<T> = listOf(first, second, third, fourth)

Quintuple (lets you pass 5 objects)

import java.io.Serializable

/**
 * Created by nalcalag on 09/02/2019.
 * 
 * Represents a quintet of values
 *
 * There is no meaning attached to values in this class, it can be used for any purpose.
 * Quintuple exhibits value semantics
 *
 * @param A type of the first value.
 * @param B type of the second value.
 * @param C type of the third value.
 * @param D type of the fourth value.
 * @param E type of the fifth value.
 * @property first First value.
 * @property second Second value.
 * @property third Third value.
 * @property fourth Fourth value.
 * @property fifth Fifth value.
 */
data class Quintuple<out A, out B, out C, out D, out E>(
        val first: A,
        val second: B,
        val third: C,
        val fourth: D,
        val fifth: E
) : Serializable {

    /**
     * Returns string representation of the [Quintuple] including its [first], [second], [third], [fourth] and [fifth] values.
     */
    override fun toString(): String = "($first, $second, $third, $fourth, $fifth)"
}

/**
 * Converts this quintuple into a list.
 */
fun <T> Quintuple<T, T, T, T, T>.toList(): List<T> = listOf(first, second, third, fourth, fifth)
Related