Search code examples
kotlinvert.x

Kotlin function call deference between () and {} when lambda as last parameter


import io.vertx.core.Vertx
import io.vertx.core.http.HttpMethod
import io.vertx.ext.web.Router
import io.vertx.ext.web.handler.CorsHandler

class RestfulServer(
    vertx: Vertx,
    private val ipAddress: String,
    private val port: Int
) {
    private val httpServer = vertx.createHttpServer()
    private val router: Router = Router.router(vertx)

    init {
        corsHandling()
        createRouter()
    }

    private fun corsHandling(): Route =
        router.route().handler {
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        }

    private fun createRouter() =
        router.get("/").blockingHandler { ctx ->
            val response = ctx.response()
            response.putHeader("content-type", "application/json")
            response.end("""{}""")
        }

    fun listen() {
        httpServer.requestHandler(router).listen(port, ipAddress)
    }

    fun close() {
        httpServer.close()
    }
}

When I run the above code, the rest API call hangs in the browser, But if I comment out the function corsHandling(), everything works fine.

I found that it's not a problem with CorsHandler but with how I call that function in kotlin.

Working function:

private fun corsHandling(): Route =
        router.route().handler( // here I use ()
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        )

This one hangs:

private fun corsHandling(): Route =
        router.route().handler{ // here I use {}
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        }

As you can see the only difference is {} instead of () in router.route().handler call. In kotlin, you can omit the function call if the lambda is your last argument.

Might be this question more to Kotlin instead of Vert.x

It's the function definition of handler https://vertx.io/docs/apidocs/io/vertx/ext/web/Route.html#handler-io.vertx.core.Handler-


The actual problem is I'm calling handler function like handler({{lambda}})

@ivo has the answer already but just clarify with a simple example,

fun takesSingleLambda(func: ()-> Unit) {
    func()
}

fun main() {
    println("1")
    takesSingleLambda(
        returnsLambda()
    )
    println("2")
    takesSingleLambda{
        returnsLambda()
    }
    println("3, which does exactly the same as 2")
    takesSingleLambda({
        returnsLambda()
    })
}

fun returnsLambda() = { //this is similar to CorsHandler.create("*").allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
    println("executing")
}

Solution

  • To say it simple:

    functionName{
    
    }
    

    is identical to

    functionName({
    
    })
    

    , not to

    functionName(
    
    )
    

    So when you write

        router.route().handler{
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        }
    

    you are actually writing

        router.route().handler({
            CorsHandler
                .create("*")
                .allowedMethods(mutableSetOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS))
        })
    

    Which is, you are wrapping a lambda in another lambda. When it then tries to execute the handler it simply is creating the handler that you wanted it to handle, instead of executing it. I hope that makes sense.