https://kotlinlang.org logo
#announcements
Title
# announcements
a

Antanas A.

08/06/2019, 9:08 AM
Hello, is it possible to have by lazy property which calls suspend function for a value? Eg
Copy code
val context by lazy { contextProvider() }
where contextProvider() is suspend function
d

diesieben07

08/06/2019, 9:10 AM
That would require the getter for
context
to be
suspend
as well. Since suspend getters / setters aren't supported yet this is not possible
a

Antanas A.

08/06/2019, 9:12 AM
understood
maybe, for alternative there is some function memoization mechanism inside kotlin sdk?
Problem is that I had source where only one place required call to contextProvider() which is expensive
and now there are couple places where contextProvider's value are needed
g

gildor

08/06/2019, 9:16 AM
Just use async for this, it has almost the same semantics, just use await() instead of direct property access
a

Antanas A.

08/06/2019, 9:16 AM
I wish to write something as: val contextProviderFun = memoized {contextProvider() }
contextProviderFun()
g

gildor

08/06/2019, 9:16 AM
Copy code
val context = async(start = Lazy) { contextProvider() }
a

Antanas A.

08/06/2019, 9:16 AM
!
legit 🙂
thanks!
g

gildor

08/06/2019, 9:17 AM
To get result use it directly; context.await()
a

Antanas A.

08/06/2019, 9:17 AM
I do not know is this semantics is not too advanced, but will work in my case
g

gildor

08/06/2019, 9:17 AM
Or abstract with some suspend function
a

Antanas A.

08/06/2019, 9:17 AM
:))
could be some
memo
inline fun inside kotlin sdk for that
g

gildor

08/06/2019, 9:18 AM
Like: suspend fun context() = context.await()
And how would this memo work? Looks exactly as existing Lazy
a

Antanas A.

08/06/2019, 9:19 AM
g

gildor

08/06/2019, 9:19 AM
How is that different from lazy?
a

Antanas A.

08/06/2019, 9:20 AM
Lazy do not allow suspend functions
just by that
g

gildor

08/06/2019, 9:20 AM
I mean memoize is not hard, but you still need a place to keep instance of this function
Than async!
I don't see in docs that this memoize from lodash is asyncronous
a

Antanas A.

08/06/2019, 9:21 AM
it will do the same, but actually concepts is not the same, Memoization
g

gildor

08/06/2019, 9:21 AM
Why not the same?
a

Antanas A.

08/06/2019, 9:22 AM
no, but in general, memoization - is storing computed value by function and not calling function again if arguments is the same
async - is async
😄
g

gildor

08/06/2019, 9:22 AM
This is exactly what async does
You can create own coroutine builder with name memoize
a

Antanas A.

08/06/2019, 9:22 AM
to be precise async uses memoization
but it's not memoization
g

gildor

08/06/2019, 9:22 AM
🤔
I mean memoization itself is easy
But if you add asyncronous background job to it become not so easy at all
a

Antanas A.

08/06/2019, 9:23 AM
g

gildor

08/06/2019, 9:23 AM
I know what is memoization
a

Antanas A.

08/06/2019, 9:23 AM
oh, you mean that in async context
memoization is not memoization by definition
but from a callers perspective there should be no worry if it's async or not with suspend functions
just must be called inside some coroutine context
g

gildor

08/06/2019, 9:26 AM
It's not so easy, it would be against structured concurrency, because you always should be explicit about scope and pure suspend function will not work
a

Antanas A.

08/06/2019, 9:27 AM
wait I'll write a little example of how I'm imagining this
Copy code
suspend fun <R>memoize(block: suspend () -> R): () -> R {
    val Undef = object {}
    var value: Any = Undef
    return {
        if (value === Undef) value = block()
        value
    }
}
g

gildor

08/06/2019, 9:32 AM
Yeah, you can do this like that
a

Antanas A.

08/06/2019, 9:32 AM
Copy code
suspend fun context() = memoize {
    contextProvider()
}
g

gildor

08/06/2019, 9:32 AM
yes
a

Antanas A.

08/06/2019, 9:32 AM
problem that i cannot call block()
inside return { }
because it's not suspend lambda
😕
g

gildor

08/06/2019, 9:32 AM
you can
return suspend { }
a

Antanas A.

08/06/2019, 9:34 AM
Copy code
suspend fun <R> memoize(block: suspend () -> R): suspend () -> Any {
    val Undef = object {}
    var value: Any = Undef
    return suspend {
        if (value === Undef) value = block() as Any
        value
    }
}
seems this is ok
but it's only for zero argument functions
g

gildor

08/06/2019, 9:34 AM
you can just create N overrides for different arguments
a

Antanas A.

08/06/2019, 9:35 AM
and it not looks complicated as it will execute everything on the same coroutine
but it could be inside kotlin std
g

gildor

08/06/2019, 9:35 AM
why complicated and what is problem with the same coroutine
I don’t think that it really worth to add to stdlib
but if you think it’s useful, just create own library
a

Antanas A.

08/06/2019, 9:36 AM
I think Arrow library should have these
g

gildor

08/06/2019, 9:36 AM
Yes, probably
Funktionale had it
a

Antanas A.

08/06/2019, 9:37 AM
Copy code
suspend fun <R> (suspend () -> R).memoize(): suspend () -> Any {
    val Undef = object {}
    var value: Any = Undef
    return suspend {
        if (value === Undef) value = this() as Any
        value
    }
}
and it could be extension function on any function (maybe too much)
this() as Any
Is invalid. R may be null
a

Antanas A.

08/06/2019, 9:38 AM
oh, shoud be "Any?" yes?
these implementations (Funktionale,Arrow) are not suspend friendly
Copy code
suspend inline fun <R> (suspend () -> R).memoize(): suspend () -> R {
    val Undef = object {}
    var value: Any? = Undef
    return suspend {
        if (value === Undef) value = this() as Any?
        value as R
    }
}
and usage:
Copy code
val lazyContextProvider = contextProvider.memoize()
/// ...
val context = lazyContextProvider()
b

Big Chungus

08/06/2019, 9:49 AM
why can't you use runBlocking block in lazy property initializer block?
Copy code
val context by lazy { runBlocking{ contextProvider() } }
g

gildor

08/06/2019, 9:51 AM
Yes, it should be
Any?
Oh, it’s blocks the thread! I don’t think that it’s expected behavior
b

Big Chungus

08/06/2019, 9:53 AM
I think the OPs issue is that he needs to retreive the data from suspend function, which requires coroutine scope. Not an async lazy property
g

gildor

08/06/2019, 9:55 AM
You don’t need coroutine scope to call suspend function, you need suspend lambda Anyway. block thread just make coroutines useless in this case. It can be valid in some cases, but always should be explicit
these implementations (Funktionale,Arrow) are not suspend friendly
@Antanas A. Yes, I know, there is no way to make them suspend friendly, only by generating suspend version of them, If you use Arrow, you can create a feature requests about it
a

Antanas A.

08/06/2019, 10:01 AM
We're using runBlocking only in tests and in integration with legacy (thread based) libraries
No, for us, the Arrow is too advanced today :))
as team is currently only in OOP thinking
d

Dico

08/06/2019, 1:33 PM
suspend operator fun getValue(...)
is invalid?
I guess so
3 Views