3

我正在尝试创建一个具有两种通用类型的函数:一种是具体化的,另一种是从其使用的上下文中派生的(因为它是一个扩展函数):

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
    }

想法是调用仅提及具体类型的函数,例如:

a.bypassLeft<NoResultException> { "" }

其中“a”是 Either<Throwable,String> 类型的对象

但是编译器并没有让我放弃它,并且要求我指定两种泛型类型,而不是从调用函数的对象中派生第二种类型。这似乎是一件很合理的事情,但也许我错了......

这有可能实现吗?如果是这样,我做错了什么?

4

1 回答 1

2

目前不可能使用一个函数来赋予单个类型参数并让另一个类型参数推断出来。如果您通过将实现更改为不使用接收器类型来键入 lambda 参数,则可以实现您想要的。

我在那里添加了一个额外的 impl,它显示了类型 args 也可以部分应用于类或其他周围范围。

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)

}
于 2021-12-14T19:30:32.860 回答