Hi folks, I test the Context Receivers and I notic...
# language-evolution
s
Hi folks, I test the Context Receivers and I notice that Generic types are acceptable but is not working as intended, there is any issue tracking generic types in Context Receivers ?
Example function:
Copy code
fun <R, T1, T2> withContext(
    param1: T1,
    param2: T2,
    block: context(T1, T2) () -> R,
): R {
    return with(param1) {
        with(param2) {
            block()
        }
    }
}
The issue is KT-51243 the compiler is removing any type parameters that don't appear in the main signature of the lambda and only appear in the context bit, resulting in that lambda having no idea that those parameters exist
Workaround is in the linked thread, but TL;DR:
Copy code
sealed interface TypeWrapper<out A> {
    object IMPL: TypeWrapper<Nothing>
} 
fun <R, T1, T2> withContext(
    param1: T1,
    param2: T2,
    block: context(T1, T2) (TypeWrapper<T2>) -> R,
): R {
    return block(param1, param2, TypeWrapper.IMPL)
}
Here's definitions up to 14 context receivers if you wanna use them (been using this in a toy project frequently):
Copy code
inline fun <A, R> withContexts(a: A, block: context(A) (TypeWrapper<A>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, TypeWrapper.IMPL)
}

inline fun <A, B, R> withContexts(a: A, b: B, block: context(A, B) (TypeWrapper<B>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, TypeWrapper.IMPL)
}

inline fun <A, B, C, R> withContexts(a: A, b: B, c: C, block: context(A, B, C) (TypeWrapper<C>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, R> withContexts(a: A, b: B, c: C, d: D, block: context(A, B, C, D) (TypeWrapper<D>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, R> withContexts(a: A, b: B, c: C, d: D, e: E, block: context(A, B, C, D, E) (TypeWrapper<E>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, block: context(A, B, C, D, E, F) (TypeWrapper<F>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, block: context(A, B, C, D, E, F, G) (TypeWrapper<G>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, H, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, block: context(A, B, C, D, E, F, G, H) (TypeWrapper<H>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, h, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, H, I, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, block: context(A, B, C, D, E, F, G, H, I) (TypeWrapper<I>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, h, i, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, H, I, J, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, block: context(A, B, C, D, E, F, G, H, I, J) (TypeWrapper<J>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, h, i, j, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, H, I, J, K, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, block: context(A, B, C, D, E, F, G, H, I, J, K) (TypeWrapper<K>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, h, i, j, k, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, H, I, J, K, L, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, block: context(A, B, C, D, E, F, G, H, I, J, K, L) (TypeWrapper<L>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, h, i, j, k, l, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, H, I, J, K, L, M, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, block: context(A, B, C, D, E, F, G, H, I, J, K, L, M) (TypeWrapper<M>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, h, i, j, k, l, m, TypeWrapper.IMPL)
}
inline fun <A, B, C, D, E, F, G, H, I, J, K, L, M, N, R> withContexts(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, block: context(A, B, C, D, E, F, G, H, I, J, K, L, M, N) (TypeWrapper<N>) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d, e, f, g, h, i, j, k, l, m, n, TypeWrapper.IMPL)
}
Another nicety is this
given
function that allows neater access to contexts, much better than labeled
this
because it differentiates between 2 different `List`contexts for instance with different type params:
Copy code
context(A) fun <A> given(): A = this@A
s
I basically was trying to do the same thing, I wrote a generator for that. Did not know that exist already something, thanks!
y
Yeah I also wrote a generator for this, it should be in the linked thread and the issue. Hopefully this'll be fixed soon anyways, but in the meanwhile this does the trick. BTW you can use a
List<T2>
here for instance and pass in an empty list, but I find that TypeWrapper is more explicit about its purpose nmand it has minimal overhead. The only sad thing is for other functions that both have generic contexts and a single parameter, it disallows the use of
it
, and even with the
withContexts
functions, it redefines what
it
means, which is a shame really.
❤️ 1