Kevin Crain
Kevin Crain

Reputation: 1935

Simpler or more functional way of chaining objects in Kotlin

I have created a helper method buildChain which essentially creates a chain of objects given that they implement the interface IChain<T> and set the contracts next member

The Code

interface Chain<T> {
    var next: T?

    operator fun plus(next: T): T?
}

fun <T : Chain<T>> buildChain(first: T, vararg members: T): T {
    var next: T? = null
    members.forEachIndexed { i, t ->
        if (i == 0) {
            next = first + t
        } else {
            next = next?.run { this + t }
        }
    }
    return first
}

Implementation example

data class Person(val name: String) : Chain<Person> {
    override var next: Person? = null

    override fun plus(next: Person): Person? {
        this.next = next
        return next
    }
}

fun createPersonChain()
        = buildChain(Person("Bob"), Person("Bitzy"), Person("Blitzy"))

Implementaion output example

@JvmStatic fun main(args: Array<String>) {
    var first = createPersonChain()
    // first.name = "Bob"
    // first.next.name = "Bitzy"
    // first.next.next.name = "Blitzy"
 }

Is there a functional or simpler way for acheiving the code above keeping the implementaion usage the same?

Upvotes: 8

Views: 4142

Answers (2)

ixytiris
ixytiris

Reputation: 31

Try apply{}. In the {} block pass your methods separated with ';'

Object().apply{ method1(); signUp(user) }

Upvotes: 3

hotkey
hotkey

Reputation: 147951

A functional idiom fold suits your needs well: it takes an initial item and then iterates over the other items, maintaining an accumulated value, which is updated on each item being processed with the function you provide.

In Kotlin, it is fold extension function for Iterable, Sequence or Array.

You can use it in the following way:

fun <T : Chain<T>> buildChain(first: T, vararg members: T): T {
    members.fold(first as T?) { acc, i -> acc?.let { it + i } }
    return first
}

Here first as T? cast is needed for the accumulator type to be inferred as nullable T?, because plus in your Chain<T> returns nullable value (by the way, is it necessary?).

You can also use foldRight, which just iterates in the opposite order:

fun <T : Chain<T>> buildChain(first: T, vararg members: T): T? =
        (listOf(first) + members)
            .foldRight(null as T?) { i, acc -> acc?.let { i + acc }; i }

And there are reduce and reduceRight with similar semantics but using the first and the last item respectively for the accumulator's initial value. Here's the example with reduceRight:

fun <T : Chain<T>> buildChain(first: T, vararg members: T): T? =
        (listOf(first) + members).reduceRight { i, acc -> i.apply { plus(acc) } }

Upvotes: 10

Related Questions