I have a property delegate using a context receiver:
class LoggingPropertyDelegate<T, V, L : Log>(
private var value: V,
private val toLog: T.() -> L
) : ReadWriteProperty<T, V> {
override fun getValue(thisRef: T, property: KProperty<*>) = value
context(Logger)
override fun setValue(thisRef: T, property: KProperty<*>, value: V) {
this.value = value
log(toLog(thisRef))
}
}
But when I try to use it on a property:
var myValue: Int by LoggingPropertyDelegate(0, { InfoLog("Changed to $myValue") })
I get an error that there is no suitable set
functions for the delegate. If I remove the context from the method everything works as expected.
Is it not possible to use context receivers on property delegates?
It is possible to use a property delegate that has context receivers. You just need to provide the context receiver in some way.
First, note that you should put context(Logger)
on the delegate class type, not on setValue
:
context(Logger)
class LoggingPropertyDelegate<T, V, L : Log>(
If myValue
is an instance property of some class Foo
, then you can do:
context(Logger)
class Foo {
var myValue: Int by LoggingPropertyDelegate(0) { ... }
}
Note that if Foo
is a data class, there seems to be a compiler bug that causes the compiler to crash. Perhaps context receivers are lowered to extra compiler parameters (?)
And then when instantiating Foo
, you will need to provide a Logger
:
val foo = with(someLogger) { Foo() }
// now you can access foo.myValue
(or instantiate Foo
in another function with a Logger
context receiver, of course)
If myValue
is a local variable, you can also directly use with
to introduce the context receiver instance, in addition to adding a Logger
context receiver to the enclosing function.