我想创建一个函数,该函数接受可变数量的不同类型的参数和一个闭包,并使用相同数量的参数调用闭包,每个参数对应于原始参数列表中的类型:>
fun <A, B, ...>mergeWhenValid(
arg1: Either<Problem, A>,
arg2: Either<Problem, B>,
...,
closure: (A, B, ...) -> T
): Either<Problem, T> {
// do stuff and call closure(a, b, ...)
}
我该怎么做?
答案 0 :(得分:0)
如果所有mergeWhenValid
正确,而closure
仅返回eithers
结果,否则返回firstProblem.left()
,则应使用Either.fx<Problem, T>
而不是函数。示例:
Either.fx<Problem, String> { "${eitherInt.bind()} ${eitherDouble.bind()} ${eitherFloat.bind()}" }
如果您的逻辑更为复杂,并且您需要以某种方式处理所有eithers
,则可以通过创建特殊的合并DSL来做到这一点:
fun <R> mergeWhenValid(block: MergeWhenValidScope.() -> R): R = MergeWhenValidScope().block()
class EitherProblem<out T>(internal val either: Either<Problem, T>)
class MergeWhenValidScope {
private val eithers = mutableListOf<Either<Problem, *>>()
operator fun <T> Either<Problem, T>.component1(): EitherProblem<T> {
eithers += this
return EitherProblem(this)
}
private fun doStuff(): Option<Problem> {
// you can use `eithers` here and choose one of their problems or create a new one
// if you return None, it will assume that all `eithers` are right,
// otherwise, problem will be wrapped in Either and returned
return eithers.asSequence().mapNotNull { it.swap().getOrElse { null } }.firstOption()
}
fun <R> combine(block: CombinerScope.() -> R): Either<Problem, R> =
doStuff().map { it.left() }.getOrElse { CombinerScope.block().right() }
object CombinerScope {
operator fun <T> EitherProblem<T>.invoke() = either.getOrHandle {
error("Unexpected problem $it")
}
}
}
用例:
mergeWhenValid {
val (int) = eitherInt
val (double) = eitherDouble
val (float) = eitherFloat
combine { "${int()} ${double()} ${float()}" }
}
或者通过将所有eithers
添加到某个对象的流水线函数:
fun <T> mergeWhenValid() = MergeWhenValidInit<T>()
class MergeWhenValidInit<T> {
operator fun <A> invoke(either: Either<Problem, A>): MergeWhenValid<A, T, T> =
MergeWhenValid(either, listOf(either)) { it }
}
class MergeWhenValid<A, B, C>(
private val either: Either<Problem, A>,
private val eithers: List<Either<Problem, *>>,
private val previous: (B) -> C // is allowed to be called only if all `eithers` are right
) {
private fun doStuff(): Option<Problem> {
// you can use `eithers` here and choose one of their problems or create a new one
// if you return None, it will assume that all `eithers` are right,
// otherwise, problem will be wrapped in Either and returned
return eithers.asSequence().mapNotNull { it.swap().getOrElse { null } }.firstOption()
}
operator fun invoke(block: (A) -> B): Either<Problem, C> =
doStuff().map { it.left() }.getOrElse { requireC(block).right() }
operator fun <D> invoke(either: Either<Problem, D>): MergeWhenValid<D, (A) -> B, C> =
MergeWhenValid(either, eithers + either) { next -> requireC(next) }
private fun requireC(next: (A) -> B): C = previous(next(either.getOrHandle {
error("Unexpected problem $it")
}))
}
用例:
mergeWhenValid<String>()(eitherInt)(eitherDouble)(eitherFloat)() { float ->
{ double -> { int -> "$int $double $float" } }
}
注意:最后一种方法颠倒了参数的顺序,并且还迫使您写{ c -> { b -> { a -> ... } } }
而不是{ c, b, a -> ... }
。