Estoy tratando de crear una función que tenga dos tipos genéricos: uno cosificado y otro derivado del contexto de su uso (ya que es una función de extensión):
inline fun <reified E, A> Either<Throwable, A>.bypassLeft(transformation: Throwable.() -> A): Either<Throwable, A> = when (this) { is Either.Left -> when (value) { is E -> value.transformation().right() else -> this } else -> this }La idea sería llamar a la función solo mencionando el tipo cosificado, algo como:
a.bypassLeft<NoResultException> { "" }Donde "a" es un objeto de tipo Cualquiera <Throwable,String>
Pero el compilador no me permite hacerlo y requiere que especifique ambos tipos genéricos, en lugar de derivar el segundo del objeto que llama a la función. Parecía bastante razonable que fuera posible, pero tal vez me equivoque...
¿Es esto posible de lograr? Si es así, ¿qué estoy haciendo mal?
Actualmente no es posible con una función atribuir un solo tipo de argumento y dejar el otro inferido. Puede lograr lo que desea si escribe los argumentos lambda cambiando su implementación para no usar un tipo de receptor.
Lancé allí un impl adicional que muestra cómo los argumentos de tipo también se pueden aplicar parcialmente con una clase u otro ámbito circundante.
import arrow.core.Either import arrow.core.right inline fun <reified E : Throwable, A> Either<Throwable, A>.bypassLeft( transformation: (E) -> A //changed to regular arg not receiver ): Either<Throwable, A> = when (this) { is Either.Left -> when (val v = value) { //name locally for smart cast is E -> transformation(v).right() else -> this } else -> this } class Catch<A>(val f: () -> A) { //alternative impl with partial type app inline fun <reified E : Throwable> recover( recover: (E) -> A ): Either<Throwable, A> = Either.catch(f).fold( { if (it is E) Either.Right(recover(it)) else Either.Left(it) }, { Either.Right(it) } ) } suspend fun main() { val x: Either<Throwable, Int> = Either.Left(StackOverflowError()) val recovered = x.bypassLeft { s: StackOverflowError -> //here infers E 0 // here infers A } println(recovered) // Either.Right(0) val notRecovered: Either<Throwable, Int> = Catch { throw NumberFormatException() 1 }.recover<StackOverflowError> { 0 } println(notRecovered) // Either.Left(java.lang.NumberFormatException) }