Допустим, у меня есть тип данных, аналогичный Validated
, называемый ValRes
sealed class ValRes<out E, out A> {
data class Valid<A>(val a: A) : ValRes<Nothing, A>()
data class Invalid<E>(val e: E) : ValRes<E, Nothing>()
}
Если у меня есть два значения типа ValRes
, и я хочу объединить их, накапливая ошибки, которые я мог бы написать функция, подобная этой:
fun <E, A, B> tupled(
a: ValRes<E, A>,
b: ValRes<E, B>,
combine: (E, E) -> E
): ValRes<E, Pair<A, B>> =
if (a is Valid && b is Valid) valid(Pair(a.a, b.a))
else if (a is Invalid && b is Invalid) invalid(combine(a.e, b.e))
else if (a is Invalid) invalid(a.e)
else if (b is Invalid) invalid(b.e)
else throw IllegalStateException("This is impossible")
- , если оба значения
Valid
Я строю пару из двух значений - , если одно из них недопустимо, я получаю новый
Invalid
экземпляр с одним значением - , если оба значения недопустимы, я использую функцию
combine
для создания Invalid
экземпляра, содержащего оба значения.
Использование:
tupled(
validateEmail("stojan"), //invalid
validateName(null) //invalid
) { e1, e2 -> "$e1, $e2" }
Это работает в общем случае c, независимо от типов E, A и B. Но это работает только для двух значений. Мы могли бы построить такую функцию для N значений типа ValRes
.
Теперь вернемся к стрелке:
Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
tupled
аналогично map
(с жестко закодированной функцией успеха) , va
и vb
здесь аналогичны a
и b
в моем примере. Вместо того, чтобы возвращать пару значений, здесь у нас есть пользовательская функция (fn
), которая объединяет два значения в случае успеха.
Объединение ошибок:
interface Semigroup<A> {
/**
* Combine two [A] values.
*/
fun A.combine(b: A): A
}
Semigroup
в стрелке - это способ объединения двух значений одного типа в одно значение того же типа. Похоже на мою combine
функцию. NonEmptyList.semigroup()
- это реализация Semigroup
для NonEmptyList
, в которой два списка складывают элементы в один NonEmptyList
.
. Суммируя:
- Если оба значения:
Valid
-> он объединит их, используя предоставленную функцию - Если одно значение равно
Valid
, а одно Invalid
-> возвращает ошибку - Если оба значения
Invalid
-> Использует экземпляр Semigroup
для Nel
для объединения ошибок
Под капотом это масштабируется от 2 до значений X (я полагаю, 22).