简体   繁体   English

Kotlin - 推断两个通用参数之一的类型

[英]Kotlin - Infer type for one of two generic parameters

I am trying to create a function that has two generic types: one reified, and another derived from the context of its usage (since it is an extension function):我正在尝试创建一个具有两种泛型类型的 function:一种是具体化的,另一种是从其使用的上下文中派生的(因为它是一个扩展函数):

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
    }

The idea would be to call the function just mentioning the reified type, something like:这个想法是调用 function 只是提到具体类型,例如:

a.bypassLeft<NoResultException> { "" }

In which "a" is an object of type Either<Throwable,String>其中“a”是类型为 Either<Throwable,String> 的 object

But the compiler is not letting me go away with it, and requires me to specify both generic types, instead of deriving the second one form the object calling the function.但是编译器不让我 go 离开它,并要求我指定两种泛型类型,而不是从调用 function 的 object 派生第二种类型。 It seemed quite a reasonable thing to be possible, but maybe I am wrong...这似乎是一件很合理的事情,但也许我错了......

Is this possible to achieve?这有可能实现吗? If so, what am I doing wrong?如果是这样,我做错了什么?

It's not currently possible with a function to ascribe a single type argument and leave the other inferred.目前不可能使用 function 来赋予单个类型参数并保留另一个类型参数。 You can achieve what you want if you type the lambda arguments by changing your implementation to not use a receiver type.如果您键入 lambda arguments 通过将您的实现更改为不使用接收器类型,您可以实现您想要的。

I threw in there an additional impl that shows how type args can also be partially applied with a class or other surrounding scope.我在那里添加了一个额外的 impl,它显示了类型 args 如何也可以部分应用于 class 或其他周围的 scope。

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)

}

This is possible as of Kotlin v1.7.0 with the underscore operator .Kotlin v1.7.0 开始,可以使用下划线运算符

The underscore operator _ can be used for type arguments.下划线运算符 _ 可用于 arguments 类型。 Use it to automatically infer a type of the argument when other types are explicitly specified:当显式指定其他类型时,使用它自动推断参数的类型:

interface Foo<T>
fun <T, F : Foo<T>> bar() {}

fun baz() {
    bar<_, Foo<String>>() // T = String is inferred
}

In your example, it would be possible like this:在您的示例中,可能是这样的:

a.bypassLeft<NoResultException, _> { "" }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM