I had a quick look with the Kotlin playground. Somehow it wants a concrete type assignment. As you can see in the code below you can do casting afterwards, but it shouldn’t be needed.
fun <I,O> docApply(f: (I) -> O, n: I): String = "$n -> ${f(n)}"
typealias PrinterGen<I,O> = ((I) -> O, I) -> String
val printerGen1: ((Any)-> Any, Any)-> String = ::docApply
val printerGen2: PrinterGen<Nothing, Nothing> = ::docApply
val printerGen3: PrinterGen<Any, Any> = ::docApply
val printerGen4: PrinterGen<Boolean, String> = ::docApply
val printerGen5: PrinterGen<Int, Int> = ::docApply
fun main() {
println(docApply( { n-> n * n } , 2)) // prints 2 -> 4
println(printerGen1( { n-> (n as Int) * n } , 2)) // prints 2 -> 4
println(printerGen3( { n-> (n as Int) * n } , 2)) // prints 2 -> 4
println(printerGen5( { n-> n * n } , 2)) // prints 2 -> 4
}
In this case you seem to have found a limitation in the type system for function types. Look at the following code:
interface PrinterGen {
operator fun <I,O> invoke(f: (I) -> O, n: I): String
}
typealias PrinterGenAlias<I,O> = Function2<(I) -> O, I, String>
// This is what the operator invoke looks like here
interface PrinterGenCopy<I, O>: PrinterGenAlias<I,O> {
override operator fun invoke(f: (I)-> O, n: I): String
}
object docApply : PrinterGen {
override operator fun <I,O> invoke(f: (I) -> O, n: I): String = "$n -> ${f(n)}"
}
fun PrinterGen(gen: PrinterGenAlias<Any,Any>): PrinterGen {
// Unfortunately this does create an additional object -- and needs unsafe args
return object: PrinterGen {
override operator fun <I2,O2> invoke(f: (I2) -> O2, n: I2): String {
return (gen as PrinterGenAlias<I2, O2>)(f, n)
}
}
}
fun <I,O> docApplyFun(f: (I) -> O, n: I): String = "$n -> ${f(n)}"
val printerGenObj: PrinterGen = docApply
val printerGenFun: PrinterGenAlias<Any, Any> = ::docApplyFun
val printerGenFactory: PrinterGen = PrinterGen(::docApplyFun)
fun main() {
println(docApply( { n-> n * n } , 2)) // prints 2 -> 4
println(printerGenObj( { n-> n * n } , 2)) // prints 2 -> 4
println(printerGenObj( { n-> n + n } , "foo")) // prints foo -> foofoo
println(printerGenFactory( { n-> n * n } , 2)) // prints 2 -> 4
println(printerGenFun( { n-> (n as Int) * n } , 2)) // prints 2 -> 4
}```
You can see the difference in the place of the generics of the two interfaces. Unfortunately function types can not be generic themselves. While I'm not 100% on the syntax it should be valid to do this without resorting to the workarounds as my code contains.