Using 'with(...)' Function with Multiple Receivers?

Are there any plans to have Kotlin’s built in ‘with’ function take in multiple receivers? I find that it could be useful in several scenarios.

For instance, the following code:

val args = Bundle()
args.putString(SomeActivity.stringKey, “String”)
args.putInt(SomeActivity.intKey, 12345)
args.putChar(SomeActivity.charKey, ‘c’)

Could possibly be made into:

with(args, activity) {
putString(stringKey, “String”)
putInt(intKey, 12345)
putChar(charKey, ‘c’)
}

No, there are no such plans.

You can nest with functions to add implicit receivers to scope, like:

with(args) {
    with(activity) {
         putString(stringKey, "String")
    }
}

However I would not recommend abusing with function like this, as it makes unclear which property/function refers to which implicit receiver.

Very recently a friend asked me for this same thing.

Does this suit your needs?

inline fun <T> with(vararg receivers: T, block: T.() -> Unit) {
    for (receiver in receivers) receiver.block()
}

You should be able to use this function for your example perfectly. :slight_smile:

@Jire Unfortunately this does not work. But I understand this is a tradeoff between clarity and convenience. I’m sure Kotlin chose clarity here by allowing only one receiver in a ‘with’ function. I must agree with Ilya that having multiple receivers will make it unclear with prop/func refers to with receiver.

There was a similar discussion recently. Maybe my suggestion there could help you: About "with" statement - #7 by medium

This question might be interesting again now that we have context receivers.

Usecase:
I want to call context (A, B, ..., Z) fun foo(), but neither A, B, …, Z is currently in my scope

Usage:
I wish i’t be as easy as

with(a, b, ..., z) {
    foo()
}
1 Like

You can roll your own very easily right now:

@Suppress("SUBTYPING_BETWEEN_CONTEXT_RECEIVERS") // Hopefully this error gets fixed soon, but basically it happens when you have generic receivers that "might", but don't necessarily, have a subtyping relation
inline fun <A, B, R> with(a: A, b: B, block: context(A, B) () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b)
}
// repeat the above declaration for however many times you want.
5 Likes