使用 Arrow 创建具有错误处理的对象构建器 - 模式匹配多个 Either

Posted

技术标签:

【中文标题】使用 Arrow 创建具有错误处理的对象构建器 - 模式匹配多个 Either【英文标题】:Creating an object builder with error handling using Arrow - Pattern match multiple Eithers 【发布时间】:2022-01-15 02:37:01 【问题描述】:

我有课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 或类似方法)并避免重复值(例如通过重新创建元组)的解决方案,因为这可能会导致拼写错误并使以后添加/删除属性变得更加困难。

【问题讨论】:

【参考方案1】:

首先你需要将build的签名改为:

fun build() : Either<List<ABuilderException>, A>

这样做的原因是因为Either 是右偏的——mapflatMap 等函数在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 中,您可以访问 有效 值(IntStringString)并且可以创建有效对象。

toValidatedNel() 这里从Validated&lt;String, String&gt; 转换为Validated&lt;Nel&lt;String&gt;, String&gt;,其中Nel 是一个不能为空的列表。将错误累积为 List 很常见,因此它是内置的。

有关更多信息,您可以查看文档中的Error Handling tutorial。

【讨论】:

谢谢,解决了。您的解释很有帮助和教育意义!

以上是关于使用 Arrow 创建具有错误处理的对象构建器 - 模式匹配多个 Either的主要内容,如果未能解决你的问题,请参考以下文章

设计模式---对象创建模式之构建器模式(Builder)

使用 SCSS 创建具有动态方向的三角形

如何使用构建器模式通过 Scala 宏返回内部对象

应用程序加载器 - 在错误的应用程序构建上推送构建

具有 3000 个标签和 1000000 行的分类器,内存错误

arrow python处理日期时间