Существует пример для Validated , который показывает случай, когда мы можем составить ошибки проверки.
Для вашего случая (я собираюсь предположить что-то здесь, например, поля, доступные вкнига) Я думаю, это будет выглядеть примерно так:
data class Book(val title: String, val authorName: String, val pageCount: Int)
Здесь мы создаем ошибки с определением полугруппы для него:
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))