【问题标题】:Validate an object with Arrow-kt使用 Arrow-kt 验证对象
【发布时间】:2019-10-06 15:23:22
【问题描述】:

我有一个对象(书),事件应该更新哪些字段(作者已更改)。假设该书的作者字段仅在作者结婚并改名时更改,但如果作者刚搬到新城市,则该书不会更改。

在这个简单的例子中,我可以检查book.authorName == event.author.name 是否返回Either<NothingChangedFailure, Book>。但是我怎么能检查不止一个字段呢?如果我继续使用 Either,该过程将在遇到的第一个 NothingChangedFailure 处停止,但我想汇总所有更新,并且只有在书中的所有字段都没有更改时才返回 NothingChangedFailure

我尝试使用OptionEither 并继续阅读Validated,但如果出现单个故障,它们似乎都失败了整个结果。那么有没有我看不到的选项?

【问题讨论】:

    标签: kotlin arrow-kt


    【解决方案1】:

    Validated 上有一个示例,显示了我们可以组合验证失败的情况。

    对于你的情况(我会在这里假设一些事情,比如书中可用的字段)我猜它看起来像:

    data class Book(val title: String, val authorName: String, val pageCount: Int)
    

    在这里,我们使用 Semigroup 的定义来创建错误:

    sealed class BookValidationError {
      data class PropertyNotChanged(val propertyName: String) : BookValidationError()
      data class Multiple(val errors: Nel<BookValidationError>) : BookValidationError()
    }
    
    object BookValidationErrorSemigroup : Semigroup<BookValidationError> {
      override fun BookValidationError.combine(b: BookValidationError): BookValidationError = when {
          this is Multiple && b is Multiple -> Multiple(errors + b.errors)
          this is Multiple && b !is Multiple -> Multiple(errors + b)
          this !is Multiple && b is Multiple -> Multiple(this.nel() + b.errors)
          else -> BookValidationError.Multiple(NonEmptyList(this, b))
      } 
    }
    

    然后我们可以为错误类型定义相关的ApplicativeError

    private val bookApplicativeError : ApplicativeError<ValidatedPartialOf<BookValidationError>, BookValidationError> = 
      Validated.applicativeError(BookValidationErrorSemigroup)
    

    我们将它与一个辅助类结合在一起:

    class BookValidation(
      private val book: Book
    ) : ApplicativeError<ValidatedPartialOf<BookValidationError>, BookValidationError> by bookApplicativeError {
    
        fun <T> fieldIsNot(name: String, actualValue: T, incorrectValue: T): Kind<ValidatedPartialOf<BookValidationError>, Book> =
            if(actualValue == incorrectValue) raiseError(BookValidationError.PropertyNotChanged(name))
            else just(book)
    
    }
    

    和一个易于访问的扩展功能:

    fun Book.validateThat(titleIsNot : String, authorNameIsNot: String, pageCountIsNot: Int) = 
        with(BookValidation(this)) {
            map(
                fieldIsNot("title", title, titleIsNot), 
                fieldIsNot("authorName", authorName, authorNameIsNot),
                fieldIsNot("pageCount", pageCount, pageCountIsNot)
            ) { this@validateThat }.handleErrorWith { 
                raiseError(it) 
            }
        }
    
    

    那么,如果你像这样执行它:

    fun main() {
        Book("a", "b", 123).validateThat(
            titleIsNot = "c",
            authorNameIsNot = "d",
            pageCountIsNot = 124
        ).let(::println)
        Book("a", "b", 123).validateThat(
            titleIsNot = "a",
            authorNameIsNot = "b",
            pageCountIsNot = 123
        ).let(::println)
        Book("a", "b", 123).validateThat(
            titleIsNot = "c",
            authorNameIsNot = "b",
            pageCountIsNot = 124
        ).let(::println)
    }
    

    第一个有效,输出如下:

    Valid(a=Book(title=a, authorName=b, pageCount=123))
    

    但是第二个会输出:

    Invalid(e=Multiple(errors=NonEmptyList(all=[PropertyNotChanged(propertyName=pageCount), PropertyNotChanged(propertyName=title), PropertyNotChanged(propertyName=authorName)])))
    

    在这个Invalid 实例中,我们有一个 NonEmptyList,其中包含所有未通过验证的字段。如果我们重新格式化一下输出,我们可以看到它们:

    Invalid(e=Multiple(
      errors=NonEmptyList(all=[
        PropertyNotChanged(propertyName=pageCount), 
        PropertyNotChanged(propertyName=title), 
        PropertyNotChanged(propertyName=authorName)
      ])
    ))
    

    现在,对于第三种情况,由于其中只有一个保持不变,我们得到以下输出:

    Invalid(e=PropertyNotChanged(propertyName=authorName))
    

    【讨论】:

    • 啊,我明白了!将尽快尝试。 +1 和谢谢!
    • 这非常适合验证整本书,但我仍然无法确定属性是否已更改?我需要将潜在变化的总和与错误的总和进行比较?如果错误少于潜在的变化,这本书已经改变了?
    • 正如它所写的,这个解决方案只有在所有值都没有改变的情况下才会通过。也可以通过一些修改为每个字段创建多个错误,以便您可以搜索该特定故障的结果。它可能会使用反射来概括。我将对其进行更新以简化结果。
    • 用更多细节更新了示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    相关资源
    最近更新 更多