Home > Software engineering >  Compose blocks with different dispatch receivers
Compose blocks with different dispatch receivers

Time:12-07

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

  • Is this possible with the proposed definition of y?
  • If yes, what should be the type of l?
  • If not, is there some other definition where I can get access to multiple dispatch receivers inside a lambda?

Note: This is related to my other SO question

CodePudding user response:

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.

  • Related