Why does Kotlin need function reference syntax?

2019-06-16 17:13发布

问题:

Kotlin docs stated that it supports higher-order functions. Why would the language even need a ::function syntax when passing a top level function as an argument?

Given:

fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // here.

Why not just

fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(isOdd)) // simple and makes more sense

More on the function reference syntax here.

回答1:

Kotlin language design tries to avoid ambiguous situations where some absence of something could be both correct and incorrect syntax at the same time. For example if you allowed the proposed syntax:

isOdd     // error, function invocation expected isOdd(...)
isOdd     // not error, you have a function reference

The :: is a clear signal as to the intent. Because of this, you get only an error in the isOdd case because you now have possibilities that do not overlap:

isOdd      // error, function invocation expected isOdd(...)
::isOdd    // function reference
isOdd()    // error, missing parameter x
isOdd(x)   // function call

This is why Kotlin avoids things that lead to ambiguous states. Your eyes can also quickly pick up the problem, just as the IDE and static analysis can, just as the compiler does. If you start allowing this looser syntax you will start running into compounded ambiguities such as when using as infix functions and so on. Language design is more complicated than "oh, let's make them type less characters" because the complexity matrix is much larger than you imagine if you only look at one use case ignoring all the others.

Why the accepted answer above is incorrect:

The currently accepted answer says this is a namespace issue, that is not correct. In that sample they use:

val isOdd : (Int) -> Boolean = { x -> x % 2 != 0 }

And claim it works without the :: because of namespacing. It actually works because this is already a function reference and was declared as a function reference and therefore does not need an operator to make it a function reference. However, the function is not a reference. It is a function. It needs an operator :: to convert it into a reference to the function which is what is required when using it as a predicate for the filter method.

The claim in that answer is misleading and untrue.



回答2:

Because Java (and, therefore, Kotlin) uses separate namespaces for fields and methods, you need :: to avoid ambiguities. Example:

val isOdd : (Int) -> Boolean = { x -> x % 2 != 0 }
fun isOdd(x: Int): Boolean {
    return x % 2 != 0
}

val odds = listOf(1,2,3).filter(isOdd) // uses the val version
val otherOdds = listOf(1,2,3).filter(::isOdd) // uses the fun version


标签: kotlin