如何使用带左方差注释的 flatMap vavr Either

How to flatMap vavr Either with left variance annotated

我的代码

open class Fail(override val message: String, override val cause: Throwable?) : RuntimeException(message, cause)

data class ValidationFail(override val message: String, override val cause: Throwable?) : Fail(message, cause)

以后会在那里定义更多的失败

我有两个功能

fun fun1(): Either<out Fail, A>
fun fun2(a: A): Either<out Fail, B>

当我尝试像这样调用它们时 fun1().flatMap{fun2(it)} 我得到了

Type mismatch: inferred type is (A!) -> Either<out Fail, B> but ((A!) -> Nothing)! was expected. Projected type Either<out Fail, A> restricts use of public final fun <U : Any!> flatMap(p0: ((R!) -> Either<L!, out U!>!)!): Either<L!, U!>! defined in io.vavr.control.Either

来自 vavr 的代码:

default <U> Either<L, U> flatMap(Function<? super R, ? extends Either<L, ? extends U>> mapper) {
    Objects.requireNonNull(mapper, "mapper is null");
    if (isRight()) {
        return (Either<L, U>) mapper.apply(get());
    } else {
        return (Either<L, U>) this;
    }
}

我想我有这个错误是因为 flatMap 定义中有 L 而不是 ? extends L

有什么解决方法吗?

在您的特定情况下,您可以通过从 fun1fun2 return 类型中删除 out 差异来使其编译。 You shouldn't use wildcard types as return types anyway.

但是,如果您以这种方式定义 fun1fun2,那将无济于事:

fun fun1(): Either<ConcreteFail1, A>
fun fun2(a: A): Either<ConcreteFail2, B>

flatMap 签名中用 ? extends L 替换 L 也无济于事,因为 ConcreteFail2 不是 ConcreteFail1 的子类型。问题是 Either 应该是协变的,但是 Java 中没有 declaration-site variance 这样的东西。虽然有一个使用 Either#narrow 方法的解决方法:

Either.narrow<Fail, A>(fun1()).flatMap { Either.narrow(fun2(it)) }

当然,它看起来很奇怪,必须提取到一个单独的扩展函数中:

inline fun <L, R, R2> Either<out L, out R>.narrowedFlatMap(
    crossinline mapper: (R) -> Either<out L, out R2>
): Either<L, R2> = narrow.flatMap { mapper(it).narrow }

其中 narrow 是:

val <L, R> Either<out L, out R>.narrow: Either<L, R> get() = Either.narrow(this)

我认为 Vavr 没有提供自己的 narrowedFlatMap 因为这个方法需要使用通配符接收器类型,所以它不能是成员方法并且必须是一个静态的,它打破了操作流水线的所有可读性:

narrowedFlatMap(narrowedFlatMap(narrowedFlatMap(fun1()) { fun2(it) }) { fun3(it) }) { fun4(it) }

但是由于我们使用 Kotlin,我们也可以通过管道传输静态(扩展)函数:

fun1().narrowedFlatMap { fun2(it) }.narrowedFlatMap { fun3(it) }.narrowedFlatMap { fun4(it) }