Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

317
Vistas
Inferencia de tipos para funciones de orden superior con tipos de devolución genéricos

El siguiente ejemplo es perfectamente legal en Kotlin 1.3.21:

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

Pero, ¿por qué la inferencia de tipos no funciona para funciones de orden superior?

 fun <T> foo() = fun(bar: T): T = bar val t: Int = foo()(1) // Compile error: Type inference failed...

Cuando se utilizan funciones de orden superior, Kotlin obliga a que el sitio de la llamada sea:

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

Incluso si el tipo de retorno de foo se especifica explícitamente, la inferencia de tipo sigue fallando:

 fun <T> foo(): (T) -> T = fun(bar: T): T = bar val t: Int = foo()(1) // Compile error: Type inference failed...

Sin embargo, cuando el parámetro de tipo genérico se comparte con la función externa, ¡funciona!

 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...

¿Cómo escribo la función foo para que foo()(1) se compile, donde bar es un tipo genérico?

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

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.

Así que según tengo entendido es que:

foo() <- usando información de tipo aquí

foo()(1) <- proporcionando la información aquí

Parece que la inferencia de tipos no funciona 'hacia atrás'

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

0

Para decirlo en términos simples (posiblemente demasiado simplificados), cuando llama a una función generada dinámicamente, como el valor de retorno de una función de orden superior, en realidad no es una llamada de función, es solo azúcar sintáctica para la función de invoke .

En el nivel de sintaxis, Kotlin trata los objetos con tipos de retorno como () -> A y (A, B) -> C como si fueran funciones normales; le permite llamarlos simplemente adjuntando argumentos entre paréntesis. Esta es la razón por la que puede hacer foo<Int>()(1) - foo<Int>() devuelve un objeto de tipo (Int) -> (Int) , que luego se llama con 1 como argumento.

Sin embargo, bajo el capó, estos "objetos de función" no son realmente funciones, son simplemente objetos simples con un método de operador de invoke . Entonces, por ejemplo, los objetos de función que toman 1 argumento y devuelven un valor son realmente solo instancias de la interfaz especial Function1 que se parece a esto

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

Cualquier clase con operator fun invoke se puede llamar como una función, es decir, en lugar de foo.invoke(bar, baz) puede simplemente llamar a foo(bar, baz) . Kotlin tiene varias clases integradas como esta llamada Function , Function1 , Function2 , Function<number of args> etc. que se utilizan para representar objetos de función. Entonces, cuando llamas a foo<Int>()(1) , lo que en realidad estás llamando es foo<Int>().invoke(1) . Puede confirmar esto descompilando el código de bytes.

Código de bytes de Kotlin descompilado

Entonces, ¿qué tiene esto que ver con la inferencia de tipo? Bueno, cuando llamas a foo()(1) , en realidad estás llamando a foo().invoke(1) con un poco de azúcar sintáctica, lo que hace que sea un poco más fácil ver por qué falla la inferencia. El lado derecho del operador punto no se puede usar para inferir tipos para el lado izquierdo, porque el lado izquierdo debe evaluarse primero. Entonces, el tipo de foo debe indicarse explícitamente como foo<Int> .

over 4 years ago · Santiago Trujillo Denunciar

0

Solo jugué un poco con él y compartimos algunos pensamientos, básicamente respondiendo a la última pregunta "¿Cómo escribo la función foo para que foo()(1) se compile, donde bar es un tipo genérico?":

Una solución simple, pero luego renuncia a su función de orden superior (o necesita envolverla) es tener un objeto intermediario en su lugar, por ejemplo:

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

con un método foo similar al siguiente:

 fun foo() = FooOp

Por supuesto, eso no es realmente lo mismo, ya que básicamente trabajas alrededor de la primera función genérica. Básicamente, es casi lo mismo que tener solo 1 función que devuelve el tipo que queremos y, por lo tanto, también puede inferir el tipo nuevamente.

Una alternativa a tu problema podría ser la siguiente. Simplemente agregue otra función que realmente especifique el tipo:

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

Entonces tendrán éxito los dos siguientes:

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

pero... (además de necesitar potencialmente muchas sobrecargas) no es posible definir otra función similar a la siguiente:

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

Si define esa función, le dará un error similar al siguiente:

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

¿Pero tal vez eres capaz de construir algo con eso?

De lo contrario, no tengo una respuesta de por qué se infiere y por qué no.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda