toEither

fun <T> ValidationResult<T>.toEither(): Either<NonEmptySet<ConstraintViolation>, T>

Transforms the ValidationResult to Either.Right of T, or Either.Left of ConstraintViolation list.

You can convert the result of the validation to an Either and apply transformations to it:

val validateBookName = Validator<String> { isNotBlank() }

fun normalizeBookName(bookName: String) = validateBookName(bookName)
.toEither()
.fold(
ifLeft = { "<error: ${it.head.message}>" },
ifRight = { it.trim() },
)

A non-blank string is trimmed because the validation was successful and went through the ifRight block:

normalizeBookName("  The Lord of the Rings  ") // Returns: "The Lord of the Rings"

Whereas, a blank string fails the validation and goes through the ifLeft block:

normalizeBookName("  ") // Returns: "<error: Must not be blank>"