I'm currently learning kotlin and have came into the following scenario. In Ktor server there's a method with the following signature:
fun Route.webSocket(protocol: String? = null, handler: suspend DefaultWebSocketServerSession.() -> Unit) {
webSocketRaw(protocol) {
proceedWebSocket(handler)
}
}
where I'm supposed to interact with it somehow like this:
embeddedServer(Netty, 8080) {
install(Routing) {
webSocket("/ws") {
// Handle websocket connection here
}
}
}
Meaning websocket accepts labda that's an extension method of DefaultWebSocketServerSession and has it's context. I would like to convert this lambda into a handler so I can pass it from somewhere else, I imagine it should look something like that:
embeddedServer(Netty, 8080) {
install(Routing) {
webSocket("/ws", myHandler::handle)
}
}
//...
fun suspend handle(context: DefaultWebSocketServerSession): Unit {
// Handle websocket connection here
}
So, my question is how do I convert suspend DefaultWebSocketServerSession.() -> Unit to(DefaultWebSocketServerSession) -> Unit, or how do I Implement a handler with suspend DefaultWebSocketServerSession.() -> Unit signature so I can pass it from the outside?
PS
I know I could do this
embeddedServer(Netty, 8080) {
install(Routing) {
webSocket("/ws") {
myHandler.handle(this)
}
}
}
But that doesn't feel elegant
There is no need to convert anything yourself. Kotlin converts between Method References, Function Literals and Function Literals with Receiver. Look at this example:
class A
class AHandler {
fun handle(a: A) {
println("AHandler $a")
}
}
fun useLambdaWithReceiver(lambda: A.()->Unit) {
val a = A()
lambda(a)
}
fun useNormalLambda(lambda: (A)->Unit) {
useLambdaWithReceiver(lambda)
}
fun main() {
val handler = AHandler()
useLambdaWithReceiver {
println("useLambdaWithReceiver $this")
}
useNormalLambda {
println("useNormalLambda $it")
}
useLambdaWithReceiver(handler::handle)
useNormalLambda(handler::handle)
}
Output:
useLambdaWithReceiver A#174d20a
useNormalLambda A#3c756e4d
AHandler A#2ef5e5e3
AHandler A#6d00a15d
Everything compiled and converted automatically.
So, you can just pass your handler method and it should be fine, unless there is a bug with suspend modifier I am not aware of.
Related
I am currently working on an api for my client application which needs to process http requests (using unirest) asynchronously as of now. I am new to CompletableFuture and haven't worked with anything similar up to this point. I was wondering whether the following structure makes sense:
// Request.kt (simplified)
class Request<T>(
// other variables relevant to the request such as body or path ...
private val responseType: Class<T>
) {
fun prepareRequest(action: (HttpRequest<*>) -> U): U {
// preprocesses the request, adds body if necessary and returns the request itself
}
fun executeAsync(action: (HttpResponse<T>) -> Unit) {
prepareRequest { req ->
action(req.asObjectAsync(responseType).get()) // Unirest call that (still) freezes the UI
}
}
// Builder logic ...
}
// ApiClient.kt (simplified)
abstract class ApiClient {
protected fun <T> executeAsync(req: Request<T>, action: (T) -> Unit) {
req.executeAsync { res ->
if (res.isSuccess){
action(res.body)
} else {
throw RuntimeException("res != 200")
}
}
}
}
// AuthClient.kt (simplified)
class AuthClient : ApiClient() {
fun signin(email: String, password: String, onSuccess: () -> Unit) {
executeAsync(
Request.builder(TokenModel::class.java)
.post("/signin")
.body(SignInModel(email, password))
.build()
) {
onSuccess() // this is going to refresh the UI, once the http request has been executed
}
}
}
As the call to get on CompletableFuture freezes the UI I thought of including an Executor or a Thread instead so that executeAsync in Request becomes the following:
fun executeAsync(action: (HttpResponse<T>) -> Unit) {
prepareRequest { req ->
Executors.newSingleThreadScheduledExecutor().execute {
action(it.asObjectAsync(responseType).get())
}
}
}
Is my structure overly complex or does it have to be like that? Do I need the Thread/Executor or can this be achieved in a different way?
Recently I've updated my project to Kotlin 1.5 and noticed some weird and unexpected behaviour when combining Flow with Result. Consider the following example:
// Kotlin 1.5.0
// Coroutines (kotlinx-coroutines-core) 1.5.0
class Foo
interface FooListener {
fun onSuccess(foo: Foo)
fun onFailure(error: Throwable?)
}
suspend fun collectData(listener: FooListener) {
flow { emit(Result.success(Foo())) }
.collect { result -> // for reference
when {
result.isSuccess -> listener.onSuccess(result.getOrThrow())
result.isFailure -> listener.onFailure(result.exceptionOrNull())
}
}
}
fun main() {
runBlocking {
collectData(object : FooListener {
override fun onSuccess(foo: Foo) {
println(foo)
}
override fun onFailure(error: Throwable?) {
println(error)
}
})
}
}
The above compiles but fails in runtime with:
Exception in thread "main" java.lang.ClassCastException: kotlin.Result cannot be cast to Foo
After some debugging I noticed that the collected value (result) appears to be of type Result<Result<Foo>> instead of the expected Result<Foo>:
suspend fun collectData(listener: FooListener) {
flow { emit(Result.success(Foo())) }
.collect { result ->
println(result) // Output: Success(Success(Foo#...))
when {
result.isSuccess -> listener.onSuccess(result.getOrThrow())
result.isFailure -> listener.onFailure(result.exceptionOrNull())
}
}
}
The problem disappears when I give up an idea of using externally provided listeners or callback functions in collectData and, for example, use something defined globally:
class Foo
interface FooListener {
fun onSuccess(foo: Foo)
fun onFailure(error: Throwable?)
}
val listener = object : FooListener {
override fun onSuccess(foo: Foo) {
println(foo)
}
override fun onFailure(error: Throwable?) {
println(error)
}
}
suspend fun collectData() {
flow { emit(Result.success(Foo())) }
.collect { result ->
println(result) // Output: Success(Foo#...)
when {
result.isSuccess -> listener.onSuccess(result.getOrThrow())
result.isFailure -> listener.onFailure(result.exceptionOrNull())
}
}
}
fun main() {
runBlocking {
collectData()
}
}
The above runs without any errors.
At this point it seems to me like a bug in the language, but I've been onto this for so long I'm not 100% sure and I could use a second opinion before bringing it up on Kotlin YouTrack.
I've seen Why does Kotlin crash passing generic Result parameter? and JVM / IR: ClassCastException with Result object when it is used by a generic method in a suspend call but the bug not only looks the other way around, but it is supposed to be fixed in the newest version as well.
This is tracked in https://youtrack.jetbrains.com/issue/KT-46915 and looks like the fix will be released with Kotlin 1.5.30
I have The same issue I downgraded the kotlin version from 1.5.10 to 1.4.32 and the code run correctly.
I'm looking for an elegant solution to the following.
I'd like to implement a Wrapper class that:
Accepts 2 implementations of the same Interface, and returns a new instance of that same Interface.
Any method call to the Wrapper object, tries to call the same method on the 1st implementation.
If the first call results into UnsupportedOperationException, then the 2th implementation should be used instead.
interface API {
fun getData(): String
}
class Main: API {
override fun getData(): String {
throw UnsupportedOperationException()
}
}
class Fallback: API {
override fun getData(): String {
return "data"
}
}
class Wrapper {
companion object {
fun getInstance(main: API, fallback: API): API {
// TODO
}
}
}
class Test {
#Test
fun `invokes the fallback instance`() {
val wrapper = Wrapper.getInstance(Main(), Fallback())
val response = wrapper.getData()
assertEquals(response, "data")
}
}
The best thing I have come up with so far is Delegate with Overrides:
class Wrapper(fallback: API): API by Main() {
val fallback = fallback
override fun getData(): String {
return fallback.getData()
}
}
What I don't like about this solution is that:
It requires overriding each unsupported operation
It gets quite verbose as the Interface grows into a complex multilevel structure with more sub interfaces
I'd also like to avoid Reflection for performance reasons and because this is a Kotlin Multiplatform project.
Any suggestions are appreciated.
Thanks,
Juan
Your proposed solution won't work because it will always favor the fallback for any overridden function.
There's no solution for your needs that can avoid having to manually handle every function of your interface. But you can have an intermediate function that handles the cascading selection of implementation for functions with the same signature.
class Wrapper (private val delegates: Array<out API>): API {
companion object {
fun getInstance(vararg delegates: API) = Wrapper(delegates)
}
private fun <R> delegate0Arg(function: API.() -> R): R {
for (delegate in delegates) {
try {
return delegate.function()
} catch (e: UnsupportedOperationException) {
// continue
}
}
throw UnsupportedOperationException()
}
override val name: String get() = delegate0Arg(API::name)
override fun getData(): String = delegate0Arg(API::getData)
}
But you would need additional functions to handle each unique number of arguments the interface functions have.
private fun <T, R> delegate1Arg(t: T, function: API.(t: T) -> R): R {
for (delegate in delegates) {
try {
return delegate.function(t)
} catch (e: UnsupportedOperationException) {
// continue
}
}
throw UnsupportedOperationException()
}
override fun getData(x: String) = delegate1Arg(x, API::getData)
I'm trying to use an extension function to CoroutineScope to launch some asynchronous work.
I'm not sure how to call this method from my main class, see below:
class MyService {
fun CoroutineScope.getFoo() = async(IO|Single|Default) { ... }
}
class MyProgram(val service : MyService) : CoroutineScope {
fun main() {
launch {
// Doesn't work, unresloved `service.getFoo`.
val deferred = service.getFoo() getFoo
// Works, but looks a bit odd IMO.
val deferred = with(service) { getFoo() }
deferred.await()
}
}
}
I know I could just move the async {} keyword to my main method, but in this way, the caller would have to decide the scheduler.
The service knows the nature of its work (IO/Computation bound single-threaded?, etc) and I think it should be the one deciding the scheduler.
As far as I understand your intent is to let the service specify the scheduler. Why not split the specification of the scheduler and the decision to run asynchronously?
Let the service function be suspendable and use withContext to specify the scheduler.
And let the caller decide, if the function should run asynchronously.
class MyService {
suspend fun getFoo() = withContext(Dispatchers.IO) {
//work
}
}
abstract class MyProgram(val service: MyService) : CoroutineScope {
fun main() {
launch {
val deferred = async { service.getFoo() }
//some work
deferred.await()
}
}
}
Why not make getFoo a normal function and pass in the scope:
fun getFoo(scope: CoroutineScope) = scope.async {
//work }
}
launch {
service.getFoo(this)
}
I can't use "by lazy" because the callbacks require suspendCoroutine, which borks in android if it blocks the main thread, so I have to use the following "cache the result" pattern over and over. Is there a way to wrap it in a funButUseCachedResultsIfTheyAlreadyExist pattern to encapsulate the xCached object?
private var cameraDeviceCached: CameraDevice? = null
private suspend fun cameraDevice(): CameraDevice {
cameraDeviceCached?.also { return it }
return suspendCoroutine { cont: Continuation<CameraDevice> ->
... deep callbacks with cont.resume(camera) ...
}.also {
cameraDeviceCached = it
}
}
When what I'd really like to write is
private suspend fun cameraDevice(): CameraDevice = theMagicFunction { cont ->
... deep callbacks with cont.resume(camera) ...
}
You can build a generalized solution by wrapping an async call as follows:
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineStart.LAZY
class LazySuspendFun<out T>(
scope: CoroutineScope,
private val block: suspend () -> T
) {
private val deferred = scope.async(Dispatchers.Unconfined, LAZY) { block() }
suspend operator fun invoke() = deferred.await()
}
fun <T> CoroutineScope.lazySuspendFun(block: suspend () -> T) =
LazySuspendFun(this, block)
This is a simple example of how you can use it. Note that we are able to compose them so that we use a lazy-inited value as a dependency to getting another one:
val fetchToken = lazySuspendFun<String> {
suspendCoroutine { continuation ->
Thread {
info { "Fetching token" }
sleep(3000)
info { "Got token" }
continuation.resume("hodda_")
}.start()
}
}
val fetchPosts = lazySuspendFun<List<String>> {
val token = fetchToken()
suspendCoroutine { continuation ->
Thread {
info { "Fetching posts" }
sleep(3000)
info { "Got posts" }
continuation.resume(listOf("${token}post1", "${token}post2"))
}
}
}
On the calling side you must be inside some coroutine context so you can call the suspending functions:
myScope.launch {
val posts = fetchPosts()
...
}
This solution is robust enough that you can concurrently request the value several times and the initializer will run only once.
I'll write this as an answer, since it's not possible to post much code in comments.
What you're looking for is something like this:
private suspend fun cameraDevice() = theMagicFunction {
CameraDevice()
}()
suspend fun theMagicFunction(block: ()->CameraDevice): () -> CameraDevice {
var cameraDeviceCached: CameraDevice? = null
return fun(): CameraDevice {
cameraDeviceCached?.also { return it }
return suspendCoroutine { cont: Continuation<CameraDevice> ->
cont.resume(block())
}.also {
cameraDeviceCached = it
}
}
}
Unfortunately, this will not compile, since closures cannot be suspendable, and neither are local functions.
Best I can suggest, unless I miss a solution there, is to encapsulate this in a class, if this variable bothers you too much.