如何使用带左方差注释的 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
有什么解决方法吗?
在您的特定情况下,您可以通过从 fun1
和 fun2
return 类型中删除 out
差异来使其编译。 You shouldn't use wildcard types as return types anyway.
但是,如果您以这种方式定义 fun1
和 fun2
,那将无济于事:
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) }
我的代码
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
有什么解决方法吗?
在您的特定情况下,您可以通过从 fun1
和 fun2
return 类型中删除 out
差异来使其编译。 You shouldn't use wildcard types as return types anyway.
但是,如果您以这种方式定义 fun1
和 fun2
,那将无济于事:
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) }