https://kotlinlang.org logo
#coroutines
Title
# coroutines
r

raniejade

08/13/2019, 4:42 AM
Is there any reason why suspend is not supported for delegated properties? (
suspend operator fun getValue/setValue
)
g

gildor

08/13/2019, 4:56 AM
Because suspend properties are not supported in general, at least for now
m

Marko Mitic

08/13/2019, 5:06 AM
By convention, property getter/setter should return fast and not block/suspend thread
g

gildor

08/13/2019, 5:08 AM
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

raniejade

08/13/2019, 5:30 AM
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

gildor

08/13/2019, 5:33 AM
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

raniejade

08/13/2019, 5:40 AM
memoized
values are unique per test so it's not only calculated once, hence why I'm using delegates here.
g

gildor

08/13/2019, 5:41 AM
yes, but you just create an instance of this function per test
r

raniejade

08/13/2019, 5:48 AM
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

gildor

08/13/2019, 5:50 AM
still not sure, this is just getter property, so it can be replaced with function
r

raniejade

08/13/2019, 5:50 AM
this is just getter property, so it can be replaced with function
Not sure what you mean by this
g

gildor

08/13/2019, 5:54 AM
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

raniejade

08/13/2019, 5:56 AM
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

gildor

08/13/2019, 6:01 AM
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

raniejade

08/13/2019, 6:03 AM
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

gildor

08/13/2019, 6:03 AM
yes, and I agree with you, suspend property would be useful
but it’s just impossible now
r

raniejade

08/13/2019, 6:05 AM
I guess what I want is kinda like
ThreadLocal
but unique per coroutineContext instead of per thread.
ignore me
g

gildor

08/13/2019, 6:06 AM
But coroutineContext is already like ThreadLocal
😅 1
but ThreadLocal with inheritance of context from parent coroutine
r

raniejade

08/13/2019, 6:13 AM
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) }
}
4 Views