函数是否需要可变数量的类型的args和具有相同数量的类型的args的闭包?

时间:2019-11-27 04:29:47

标签: kotlin

我想创建一个函数,该函数接受可变数量的不同类型的参数和一个闭包,并使用相同数量的参数调用闭包,每个参数对应于原始参数列表中的类型:

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, ...)
}

我该怎么做?

1 个答案:

答案 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 -> ... }