我在下面的通用函数中遇到了该函数,它需要两个Either类型和一个函数作为参数。如果两个参数均为Either.Right,则对其应用函数并返回结果;如果任何参数均为Either.Left,则其返回NonEmptyList(Either.Left)。基本上,它执行独立操作并累积错误。

fun <T, E, A, B> constructFromParts(a: Either<E, A>, b: Either<E, B>, fn: (Tuple2<A, B>) -> T): Either<Nel<E>, T> {
    val va = Validated.fromEither(a).toValidatedNel()
    val vb = Validated.fromEither(b).toValidatedNel()
    return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
}

val error1:Either<String, Int> = "error 1".left()
val error2:Either<String, Int> = "error 2".left()

val valid:Either<Nel<String>, Int> = constructFromParts(
        error1,
        error2
){(a, b) -> a+b}

fun main() {
    when(valid){
        is Either.Right -> println(valid.b)
        is Either.Left -> println(valid.a.all)
    }
}

以上代码打印
[error 1, error 2]

在函数内部,它将Either转换为ValidatedNel类型并累积两个错误
(无效(e = NonEmptyList(all = [错误1]))无效(e = NonEmptyList(all = [错误2])))

我的问题是它如何执行此操作,或者有人可以从代码中解释以下内容。
return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()

最佳答案

假设我有一个与Validated类似的数据类型,称为ValRes

sealed class ValRes<out E, out A> {
    data class Valid<A>(val a: A) : ValRes<Nothing, A>()
    data class Invalid<E>(val e: E) : ValRes<E, Nothing>()
}

如果我有两个ValRes类型的值,并且想将它们组合起来以累积错误,则可以编写如下函数:
fun <E, A, B> tupled(
            a: ValRes<E, A>,
            b: ValRes<E, B>,
            combine: (E, E) -> E
        ): ValRes<E, Pair<A, B>> =
            if (a is Valid && b is Valid) valid(Pair(a.a, b.a))
            else if (a is Invalid && b is Invalid) invalid(combine(a.e, b.e))
            else if (a is Invalid) invalid(a.e)
            else if (b is Invalid) invalid(b.e)
            else throw IllegalStateException("This is impossible")
  • 如果两个值都为Valid,则构建两个值一对
  • 如果其中之一无效,我将获得一个具有单个值
  • 的新Invalid实例
  • 如果两者均无效,我将使用combine函数来构建包含两个值的Invalid实例。

  • 用法:
    tupled(
        validateEmail("stojan"),    //invalid
        validateName(null)          //invalid
    ) { e1, e2 -> "$e1, $e2" }
    

    这以通用的方式工作,而与类型E,A和B无关。但是它仅适用于两个值。我们可以为ValRes类型的N个值构建这样的函数。

    现在回到箭头:
    Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
    
    tupledmap(具有硬编码成功功能)相似。这里的vavb与我的示例中的ab相似。这里没有返回一对值,而是有一个自定义函数(fn),该函数在成功的情况下将两个值组合在一起。

    合并错误:
    interface Semigroup<A> {
      /**
       * Combine two [A] values.
       */
      fun A.combine(b: A): A
    }
    
    

    箭头中的Semigroup是一种将来自同一类型的两个值组合到一个相同类型的值中的方法。类似于我的combine函数。 NonEmptyList.semigroup()SemigroupNonEmptyList的实现,给定两个列表,将元素一起添加到单个NonEmptyList中。

    总结一下:
  • 如果两个值都是Valid->它将使用提供的函数
  • 组合它们
  • 如果一个值为Valid而一个为Invalid->则返回错误
  • 如果两个值都为Invalid->使用Semigroup实例的Nel实例合并错误

  • 在引擎盖下,它最多可缩放2个X值(我相信是22个)。

    09-08 02:23