我有类A
:
class A (private var z: String, private var y: String, private var x: Int)
我想为它创建一个故障保护生成器。生成器应该返回Either
异常列表(例如,当值丢失时(或创建的值创建这样的东西的推荐方法是什么或者有概念上更好的方法吗?
我自己的方法:
sealed class ABuilderException {
object MissingXValue : ABuilderException()
object MissingYValue : ABuilderException()
object MissingZValue : ABuilderException()
}
import arrow.core.Either
import arrow.core.Option
import arrow.core.none
import arrow.core.some
class ABuilder {
private var x : Option<Int> = none()
private var y : Option<String> = none()
private var z : Option<String> = none()
fun withX(x : Int) : ABuilder {
this.x = x.some();
return this;
}
fun withY(y : String) : ABuilder {
this.y = y.some();
return this;
}
fun withZ(z : String) : ABuilder {
this.z = z.some();
return this;
}
fun build() : Either<A, List<ABuilderException>> {
var xEither = x.toEither { ABuilderException.MissingXValue }
var yEither = y.toEither { ABuilderException.MissingYValue }
var zEither = z.toEither { ABuilderException.MissingZValue }
// If all values are not an exception, create A
// otherwise: Return the list of exceptions
}
}
如何最好地完成build
代码
我喜欢避免深度嵌套(例如orElse
或类似方法(和避免重复值(例如通过重新创建元组(的解决方案,因为这可能会导致打字错误,并使以后更难添加/删除属性。
首先需要将build
的签名更改为:
fun build() : Either<List<ABuilderException>, A>
这样做的原因是Either
是右偏的——像map
、flatMap
等函数对Right
值进行运算,在值为Left
的情况下不进行运算。
对于组合Either
值,可以使用zip
:
val e1 = 2.right()
val e2 = 3.right()
// By default it gives you a `Pair` of the two
val c1 = e1.zip(e2) // Either.Right((2, 3))
// Or you can pass a custom combine function
val c2 = e1.zip(e2) { two, three -> two + three } // Either.Right(5)
然而,这里有一个问题,如果出现错误(其中一个是Left
(,它会很快失败,只会给您第一个错误。
为了累积错误,我们可以使用Validated
:
val x = none<Int>()
val y = none<String>()
val z = none<String>()
// Validated<String, Int>
val xa = Validated.fromOption(x) { "X is missing" }
// Validated<String, String>
val ya = Validated.fromOption(y) { "Y is missing" }
// Validated<String, String>
val za = Validated.fromOption(z) { "Z is missing" }
xa.toValidatedNel().zip(
ya.toValidatedNel(),
za.toValidatedNel()
) { x, y, z -> TODO() }
Validated
与Either
一样具有用于组合值的zip
函数。不同之处在于Validated
将累积误差。在lambda中,您可以访问有效的值(Int
、String
、String
(,并且可以创建有效的对象。
这里的toValidatedNel()
从Validated<String, String>
转换为Validated<Nel<String>, String>
,其中Nel
是不能为空的列表。累积错误作为List
是常见的,因此它内置于中
有关更多信息,您可以查看文档中的错误处理教程。