hi! Is there an existing suspend memoize function?...
# coroutines
v
hi! Is there an existing suspend memoize function? If not, is the following code idiomatic?
Copy code
fun <T> (suspend () -> T).memoize() : suspend () -> T {
    var value: T? = null
    return suspend { value ?: this().also { value = it }}
}

suspend fun work() : Int = coroutineScope {
    delay(1000)
    22
}

val memoizedWork = ::work.memoize()
e
This code has a data race on update to
value
when memoized function is called concurrently.
I also think that you actually want only one of two concurrent callers to actually “do the work” and the other one to wait.
Now, if you fix both of the above problems (waiting, races) it will have another problem. Consider two coroutines calling this function concurrently. The first one starts “work”, the other one waits. Now, what happens if the first one gets cancelled before the work finished?
To avoid this problem any kind of asynchronous-computation-memoization mechanism has to have its own scope in which the work is performed, independent of the scope of waiters. And there is a ready-to-use function that properly implements all of the above:
Copy code
val memoized = scope.async(start = CoroutineStart.LAZY) { work() }
// when you need its value:
memoized.await()
👍 7
v
Great, thanks!
Something like
suspend lazy val ...
would be nice to have though 😉
g
I would rather keep using async instead of special language feature like suspend lazy val, asynbc has much more control and you can always choose a scope for it, provide custom context (for error handling or debuging) I have feeling that this suspend lazy would just limiting factor just because it doesn’t support anything, covers only simple particular use case and very soon (right after first test for this class) will be replaced with more flexible async{}
o
you could always define a special extension function to do
scope.lazy {}
, which is probably as close as it would ever get
👍 1
1
g
also if you class implements CoroutinesScope, you will be able to use it like:
Copy code
val memoizedWork = lazy { work() }
m
suspend val
with
lazy
would be nice! I just ran into the same issue. I’d need a coroutine scope to initialize a property
val
with
.async
- but I don’t have one when initializing members. I’d like to use the scope of the first access to the property instead 🤔
105 Views