David Kubecka
David Kubecka

Reputation: 300

Compose blocks with different dispatch receivers

Setup:

interface AProvider {
    fun getA(): String
}

interface BProvider {
    fun getB(): String
}

fun a(block: AProvider.() -> Unit) {}
fun b(block: BProvider.() -> Unit) {}

With this, I can nest the two functions as follows

val x = a { b { getA(); getB() } }

Now I would like to abstract this particular pattern to a higher level function so that I can pass the inner block and call both getA() and getB() in it, e.g. something like this:

val y = { l: ??? -> a { b(l) } }

val z = y { getA(); getB() }

The questions are

Note: This is related to my other SO question

Upvotes: 1

Views: 117

Answers (1)

Sweeper
Sweeper

Reputation: 272845

You can do this with context receivers, which allows you to specify multiple receivers for one lambda. The type of y would be:

(context(AProvider, BProvider) () -> Unit) -> Unit

That is, a function that takes another function as a parameter, and returns Unit. The function parameter that it takes also returns Unit, but has AProvider and BProvider as its context receivers.

val y: (context(AProvider, BProvider) () -> Unit) -> Unit = { l -> 
    a { b { l(this@a, this@b) } } 
}

val z = y { getA(); getB() }

Notice that when we call l, we pass the context receivers, this@a and this@b, as if they are regular parameters.

This makes z a Unit as well, which is kind of weird. y returns whatever a returns after all, so perhaps you did not intend a to return Unit.

Upvotes: 1

Related Questions