Search code examples
kotlin

Cannot wrap a templated/generic function call because it requires reified and inline which does not work locally


fun <T> f(): T { return foo.bar<T>() }

This fails because Kotlin wants T to be reified and f inline.

But f is a local function and foo is a local variable outside the scope and you cannot inline a local function: "Local inline functions are not yet supported in inline functions"

This is really annoying as it seems like one cannot transparently pass through a template/type locally.

In my code I'm trying to reduce the complexity of

// Must have reified here to be able to determine the type of T 
inline fun <reified T> Long.bar(): T { 
    return when (T::class) {
        Int::class -> 43 as T       
        Float::class -> 34f as T
        else -> throw Exception("")
    }
}

fun main() {
    var foo = 3L

    fun <T> f(): T { return foo.bar<T>() }

    f<Int>()    
}

ERROR: Cannot use 'T' as reified type parameter. Use a class instead.

The entire point of f is to localize foo.bar<T>. (the real code is more complex).

Seems Kotlin's templates are not very sophisticated. Is there any way around this without a lot of trouble? (I could remove the type dependency and pass the type as a value parameter but that removes the type type checking and consistency and all that.


Solution

  • Inline local functions are not a feature in Kotlin, but there is a feature request for it on YouTrack: KT-17579.

    As an alternative, you can wrap the function within an object

    fun main() {
        var foo = 3L
    
        val local = object {
            inline fun <reified T> f(): T {
                return foo.bar<T>()
            }
        }
    
        local.f<Int>()
    }
    

    f is still local, in the sense that nothing outside of main can access it directly.

    If you really want to keep the f<Int>() syntax, you can use the invoke convention like this:

    val f = object {
        inline operator fun <reified T> invoke(): T {
            return foo.bar<T>()
        }
    }
    
    f<Int>() // this now works
    

    But you will need one object for each local function you want to declare this way.