Using functions as lamdas

I can get this to compile:

class Example {
    val myLambda =  ::addExt
    val lambdaMap : Map<String, (String) -> String> = mapOf(
        "world" to { value -> addExt(value) } 
    )

    fun addExt(value: String): String {
        return "$value.gif"
    }
}

But, I can’t seem to get this work:

class Example {
    val myLambda = ::addExt
    val lambdaMap: Map<String, (String) -> String> = mapOf(
        "world" to { value -> addExt(value) }, 
        "mars" to ::addExt
    )

    fun addExt(value: String): String {
        return "$value.gif"
    }
}

Oddly, it’s the line with “world” that is the problem. If I comment it out, it will compile.

I get the error:
Type mismatch.
Required:
KFunction1<@ParameterName String, String>
Found:
(???) → String

If I change it to:

class Example {
    val myLambda = ::addExt
    val lambdaMap: Map<String, (String) -> String> = mapOf(
        "world" to { value:String -> addExt(value) },
        "mars" to ::addExt
    )

    fun addExt(value: String): String {
        return "$value.gif"
    }
}

With the explicit, “value: String”, it will compile again.

I would expect Kotlin to implicitly figure out the return type as it did in the first example.

Bug or not a Bug?

The following line does compile in the map:

Pair<String, (String) -> String>("moon", ::addExt)

With to some type information seems to be missing.

Hold on, that’s amazing. Once the magic Pair is added, it starts to work:

    val lambdaMap : Map<String, (String) -> String> = mapOf(
        "world" to { value -> addExt(value) },
        "mars" to { v -> addExt(v) },
        Pair("pluto", { v -> myLambda(v) }),
        Pair<String, (String) -> String>("moon", ::addExt), // magic
        Pair("earth", ::addExt),
        "venus" to ::addExt
    )

What’s going on here?

Looks like the problem boils down to:

fun addExt(value: String): String = "$value.gif"
println({ value -> addExt(value) }) // cannot infer type for value
println(::addExt)

Add the type information and you see that a lambda/fun literal does not produce the same kind of thing like a function reference does:

println({ value: String -> addExt(value) })
println(fun(value: String) = addExt(value))
println(::addExt)

Prints:

(kotlin.String) -> kotlin.String
(kotlin.String) -> kotlin.String
fun Experiment.addExt(kotlin.String): kotlin.String

I think you are onto something. When these different kinds of functions are combined, type inference needs a hint for one kind or the other to compile properly. Type unification doesn’t work accross these different function kinds. I have no explanation for this.

fun addExt(value: String): String = "$value.gif"

val map1: Map<String, (String) -> String> = mapOf(
    "world" to { value -> addExt(value) }
)

val map2: Map<String, (String) -> String> = mapOf(
    "mars" to ::addExt
)

val map3: Map<String, (String) -> String> = mapOf(
    "world" to { value -> addExt(value) },
    Pair<String, (String) -> String>("moon", ::addExt),
    "mars" to ::addExt
)

val map4: Map<String, (String) -> String> = mapOf(
    "world" to { value: String -> addExt(value) },
    "earth" to { value -> addExt(value) },
    "moon" to ::addExt
)
1 Like

I’m still a kotlin newbie. Should I write an issue? Or, how do I push it forward?

I’m not sure, since I’m new to the Kotlin community. Maybe wait one or two days if somebody else adds comments to this issue. Maybe a crosspost in “language design” gets some attention to your topic. I guess that there is a technical reason for the behavior you observed. Nonetheless, type inference should infer what is inferable. :smile:

1 Like

For reference: https://youtrack.jetbrains.com/issue/KT-36940

1 Like