Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

319
Visualizações
Type inference for higher order functions with generic return types

The following example is perfectly legal in Kotlin 1.3.21:

fun <T> foo(bar: T): T = bar

val t: Int = foo(1) // No need to declare foo<Int>(1) explicitly

But why doesn't type inference work for higher order functions?

fun <T> foo() = fun(bar: T): T = bar

val t: Int = foo()(1) // Compile error: Type inference failed...

When using higher order functions, Kotlin forces the call site to be:

val t = foo<Int>()(1)

Even if the return type of foo is specified explicitly, type inference still fails:

fun <T> foo(): (T) -> T = fun(bar: T): T = bar

val t: Int = foo()(1) // Compile error: Type inference failed...

However, when the generic type parameter is shared with the outer function, it works!

fun <T> foo(baz: T) = fun (bar: T): T = bar

val t: Int = foo(1)(1) // Horray! But I want to write foo()(1) instead...

How do I write the function foo so that foo()(1) will compile, where bar is a generic type?

over 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

I am not an expert on how type inference works, but the basic rule is: At the point of use the compiler must know all types in the expression being used.

So from my understanding is that:

foo() <- using type information here

foo()(1) <- providing the information here

Looks like type inference doesn't work 'backward'

    val foo = foo<Int>()//create function
    val bar = foo(1)//call function
over 4 years ago · Santiago Trujillo Relatório

0

To put it in simple (possibly over-simplified) terms, when you call a dynamically generated function, such as the return value of a higher-order function, it's not actually a function call, it's just syntactic sugar for the invoke function.

At the syntax level, Kotlin treats objects with return types like () -> A and (A, B) -> C like they are normal functions - it allows you to call them by just attaching arguments in parenthesis. This is why you can do foo<Int>()(1) - foo<Int>() returns an object of type (Int) -> (Int), which is then called with 1 as an argument.

However, under the hood, these "function objects" aren't really functions, they are just plain objects with an invoke operator method. So for example, function objects that take 1 argument and return a value are really just instances of the special interface Function1 which looks something like this

interface Function1<A, R> {
    operator fun invoke(a: A): R
}

Any class with operator fun invoke can be called like a function i.e. instead of foo.invoke(bar, baz) you can just call foo(bar, baz). Kotlin has several built-in classes like this named Function, Function1, Function2, Function<number of args> etc. used to represent function objects. So when you call foo<Int>()(1), what you are actually calling is foo<Int>().invoke(1). You can confirm this by decompiling the bytecode.

Decompiled Kotlin bytecode

So what does this have to do with type inference? Well when you call foo()(1), you are actually calling foo().invoke(1) with a little syntactic sugar, which makes it a bit easier to see why inference fails. The right hand side of the dot operator cannot be used to infer types for the left hand side, because the left hand side has to be evaluated first. So the type for foo has to be explicitly stated as foo<Int>.

over 4 years ago · Santiago Trujillo Relatório

0

Just played around with it a bit and sharing some thoughts, basically answering the last question "How do I write the function foo so that foo()(1) will compile, where bar is a generic type?":

A simple workaround but then you give up your higher order function (or you need to wrap it) is to have an intermediary object in place, e.g.:

object FooOp {
  operator fun <T> invoke(t : T) = t
}

with a foo-method similar as to follows:

fun foo() = FooOp

Of course that's not really the same, as you basically work around the first generic function. It's basically nearly the same as just having 1 function that returns the type we want and therefore it's also able to infer the type again.

An alternative to your problem could be the following. Just add another function that actually specifies the type:

fun <T> foo() = fun(bar: T): T = bar
@JvmName("fooInt")
fun foo() = fun(bar : Int) = bar

The following two will then succeed:

val t: Int = foo()(1)
val t2: String = foo<String>()("...")

but... (besides potentially needing lots of overloads) it isn't possible to define another function similar to the following:

@JvmName("fooString")
fun foo() = fun(bar : String) = bar

If you define that function it will give you an error similar as to follows:

Conflicting overloads: @JvmName public final fun foo(): (Int) -> Int defined in XXX, @JvmName public final fun foo(): (String) -> String defined in XXX

But maybe you are able to construct something with that?

Otherwise I do not have an answer to why it is infered and why it is not.

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda