Is there any reason why suspend is not supported f...
# coroutines
r
Is there any reason why suspend is not supported for delegated properties? (
suspend operator fun getValue/setValue
)
g
Because suspend properties are not supported in general, at least for now
m
By convention, property getter/setter should return fast and not block/suspend thread
g
I believe it has nothing to do with convention
and suspend properties may be useful, it’s more like an existing limitation of compile
☝️ 2
r
I'm trying to re-write spek's execution engine to use coroutines and checking if I could use a custom
CoroutineContext.Element
to implement
memoized
values.
g
I don’t se how you use delegates there and why do you need delegate for this
you can just Deferred for memoization of values
or just make Spek memoize function suspend, it’s possible to implement and you don’t need properties or delegates
r
memoized
values are unique per test so it's not only calculated once, hence why I'm using delegates here.
g
yes, but you just create an instance of this function per test
r
that's what the current implementation is doing, But it has some limitation specially if I want to run tests in parallel - since I have a single delegate instance.
Copy code
class Memoized<T>(private val name: String,
                    private val constructor: () -> T,
                    private val destructor: (T) -> Unit) {
    private var value: T? = null
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return checkNotNull(value)
    }

    // called before a test is executed
    fun init() {
        value = constructor()
    }

    // called after a test is executed
    fun destroy() {
        destructor(checkNotNull(value))
        value = null
    }
}
g
still not sure, this is just getter property, so it can be replaced with function
r
this is just getter property, so it can be replaced with function
Not sure what you mean by this
g
I mean that you don’t need property for this, you can create instance of function that does the same, you even do not use property name in this code
r
It's being used like this:
Copy code
val a by memoized { ... }

test("test #1") {
  // a is a different instance
}

test("test #2") {
  // a is a different instance
}
what I want is to store the "value" in the
coroutineContext
and the property delegate will just look it up or set/remove it.
Copy code
class Memoized<T>(private val name: String,
                    private val constructor: () -> T,
                    private val destructor: (T) -> Unit) {
    suspend operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return valueFrom(name, coroutineContext)
    }

    // called before a test is executed
    suspend fun init() {
        setValue(name, coroutineContext, constructor())
    }

    // called after a test is executed
    suspend fun destroy() {
        destructor(removeValue(name, coroutineContext))
    }
}
Then each test will be executed with a different
coroutineContext
.
g
okay, why do you need
suspend operator fun getValue()
? why not just
suspend fun getValue()
. Because it’s property delegate, right? If it will be just an object it will be possible, again, you just cannot have suspend property, as result
suspend operator getValue
has no sense
r
because the dsl will be clunky:
Copy code
val a by memoized { ... }

test("some test") { print(a) }
vs
Copy code
val a = memoized()
test("some test") { print(a.get()) }
same thing if memoized returns a function:
print(a())
g
yes, and I agree with you, suspend property would be useful
but it’s just impossible now
r
I guess what I want is kinda like
ThreadLocal
but unique per coroutineContext instead of per thread.
ignore me
g
But coroutineContext is already like ThreadLocal
😅 1
but ThreadLocal with inheritance of context from parent coroutine
r
yeah, useful for implementing memoized where each group gets a unique value (instead of a test).
Copy code
val cachedPerGroup by memoized (CachingMode.EACH_GROUP) { ... }

group("group #1") { // launch(newCoroutineContext()) { ... }
    // cachedPerGroup instance #1
    group("group #2") { // launch(newCoroutineContext()) { ... }
        // cachedPerGroup instance #2
        test("test in group #2") { print(cachedPerGroup) }
    }

    test("test in group #1") { print(cachedPerGroup) }
}