Ben
Ben

Reputation: 4297

Pass lambda as extension method

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

Upvotes: 0

Views: 181

Answers (1)

xinaiz
xinaiz

Reputation: 7788

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.

Upvotes: 2

Related Questions