Kotlin Type-Inference Puzzler

Kotlin takes Type-Inference to the next level (at least in comparison to Java) which is great, but there’re scenarios, in which it can backfire on us.

The Riddle

fun foo(int: Int?) = {
    println(int)
}

fun main(args: Array<String>) {
    listOf(42).forEach { foo(it) }
}

And the question is: what does the puzzler() method print and why it’s not 42? Assume that the main() method gets executed.

The answer can be found at the very end of the article.

Explanation

In Kotlin, we’ve got not only a local variable type inference (which is also coming to Java) but also the return value type inference when writing single-expression methods.

Which means that if we write a method:

fun foo() : String  {
    return "42"
}

We can rewrite it using the single-expression method syntax and ignore the explicit type declaration and the return keyword:

fun foo() = "42"

So, if we have a method:

fun foo(int: Int?) : Unit {
    println(int)
    return Unit
}

we could get rid of the return type declaration, as well as the explicit return statement by leveraging single-expression method syntax, right? but we already know that the following doesn’t work:

fun foo(int: Int?) = {
    println(int)
}

The devil’s in the details but everything becomes crystal clear if we specify the return type explicitly:

fun foo(int: Int?) : () -> Unit = {
    println(int)
}

If we look closely, it can be seen that we mixed two approaches here and actually defined a method that doesn’t return Unit but a () -> Unit itself – which is simply an action that doesn’t accept any parameters and doesn’t return anything – which is semantically equivalent to returning a java.lang.Runnable instance.

This is because Kotlin utilizes curly braces not only for defining classes/methods but also for defining Lambda Expressions and { println(42) } is a valid Lambda Expression declaration:

val foo: () -> Unit = { println(42) }

So, our original example is simply a Higher-Order Function accepting an Int parameter and returning a function that prints it – when we return it in the forEach() the return value just gets ignored.

So, if we want to fix our example, we have two ways to go.

Explicitly call invoke() on the returned function:

listOf(42)
  .forEach { foo(it).invoke() }

or simply define the method properly by removing the “=” sign:

fun foo(int: Int?) {
    println(int)
}

or by leveraging the single-expression method syntax:

fun foo(int: Int?) = println(int)

That’s why I encourage my fellow team members to declare return types explicitly.

Code snippets can be found on GitHub.

Answer

The method prints nothing.

You May Also Like

New HTTP Logger Grails plugin

I've wrote a new Grails plugin - httplogger. It logs:

  • request information (url, headers, cookies, method, body),
  • grails dispatch information (controller, action, parameters),
  • response information (elapsed time and body).

It is mostly useful for logging your REST traffic. Full HTTP web pages can be huge to log and generally waste your space. I suggest to map all of your REST controllers with the same path in UrlMappings, e.g. /rest/ and configure this plugin with this path.

Here is some simple output just to give you a taste of it.

17:16:00,331 INFO  filters.LogRawRequestInfoFilter  - 17:16:00,340 INFO  filters.LogRawRequestInfoFilter  - 17:16:00,342 INFO  filters.LogGrailsUrlsInfoFilter  - 17:16:00,731 INFO  filters.LogOutputResponseFilter  - >> #1 returned 200, took 405 ms.
17:16:00,745 INFO filters.LogOutputResponseFilter - >> #1 responded with '{count:0}'
17:18:55,799 INFO  filters.LogRawRequestInfoFilter  - 17:18:55,799 INFO  filters.LogRawRequestInfoFilter  - 17:18:55,800 INFO  filters.LogRawRequestInfoFilter  - 17:18:55,801 INFO  filters.LogOutputResponseFilter  - >> #2 returned 404, took 3 ms.
17:18:55,802 INFO filters.LogOutputResponseFilter - >> #2 responded with ''

Official plugin information can be found on Grails plugins website here: http://grails.org/plugins/httplogger or you can browse code on github: TouK/grails-httplogger.