了解 kotlin 箭头库中的 Validated.applicative
Understanding Validated.applicative in kotlin arrow library
我在下面看到通用函数,它接受两个 Either
类型和一个函数作为参数。如果两个参数都是 Either.Right
则在其上应用函数并 returns 结果,如果任何参数是 Either.Left
它 returns 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=[error 1]))无效(e=NonEmptyList(all=[error 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()
tupled
类似于 map
(带有硬编码成功函数)。这里的 va
和 vb
类似于我示例中的 a
和 b
。这里我们没有返回一对值,而是有一个自定义函数 (fn
),它在成功的情况下组合两个值。
合并错误:
interface Semigroup<A> {
/**
* Combine two [A] values.
*/
fun A.combine(b: A): A
}
箭头中的 Semigroup
是一种将来自同一类型的两个值组合成同一类型的单个值的方法。类似于我的 combine
函数。 NonEmptyList.semigroup()
是 Semigroup
对 NonEmptyList
的实现,它给定两个列表,将元素添加到单个 NonEmptyList
.
总结一下:
- 如果两个值都是
Valid
-> 它将使用提供的函数组合它们
- 如果一个值是
Valid
和一个 Invalid
-> 返回错误
- 如果两个值都是
Invalid
-> 使用 Nel
的 Semigroup
实例来合并错误
在引擎盖下,它可以缩放 2 个 X 值(我相信是 22 个)。
我在下面看到通用函数,它接受两个 Either
类型和一个函数作为参数。如果两个参数都是 Either.Right
则在其上应用函数并 returns 结果,如果任何参数是 Either.Left
它 returns 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=[error 1]))无效(e=NonEmptyList(all=[error 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
.
现在回到箭头:
Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
tupled
类似于 map
(带有硬编码成功函数)。这里的 va
和 vb
类似于我示例中的 a
和 b
。这里我们没有返回一对值,而是有一个自定义函数 (fn
),它在成功的情况下组合两个值。
合并错误:
interface Semigroup<A> {
/**
* Combine two [A] values.
*/
fun A.combine(b: A): A
}
箭头中的 Semigroup
是一种将来自同一类型的两个值组合成同一类型的单个值的方法。类似于我的 combine
函数。 NonEmptyList.semigroup()
是 Semigroup
对 NonEmptyList
的实现,它给定两个列表,将元素添加到单个 NonEmptyList
.
总结一下:
- 如果两个值都是
Valid
-> 它将使用提供的函数组合它们 - 如果一个值是
Valid
和一个Invalid
-> 返回错误 - 如果两个值都是
Invalid
-> 使用Nel
的Semigroup
实例来合并错误
在引擎盖下,它可以缩放 2 个 X 值(我相信是 22 个)。