에러 누적 — zipOrAccumulate & 친구들

검증에는 첫 번째 에러가 아니라 모든 에러가 필요합니다. 여기 있는 연산들은 모든 분기를 실행하고 실패를 NonEmptyList로 이어 붙입니다 — 별도의 Validated 타입을 대체하는 Arrow 2.x의 방식입니다.

on Raise<Nel<E>>: R accumulate<R>(R Function(Accumulator<E> acc) block) R zipOrAccumulate2..5(branch1, …, combine) List<B> mapOrAccumulate<A, B>(items, transform) · A bindNel<A>(EitherNel<E, A> e) on Accumulator<E>: accumulating(block) · dependent(block) · hasErrors

강의

either<Nel<E>, _>(...) 안에서 — 에러 타입이 NonEmptyList인 모든 스코프에서 — 스코프는 누적 어휘를 갖게 됩니다:

계약은 Arrow의 것 그대로입니다. 모든 분기가 실행되고(에러는 분기 순서대로 이어 붙습니다), raise 대신 throw한 분기는 누적보다 우선하며, 첫 에러가 난 뒤로는 성공 결과를 더 이상 보관하지 않습니다 — 남은 에러를 모으기 위해 순회만 계속할 뿐입니다.

데모 1 · zipOrAccumulate2

데모 2 · accumulate — 일반형

데모 3 · mapOrAccumulate, bindNel & toEitherNel

데모 4 · dependent — 형제 값을 읽는 규칙

브랜치는 형제의 Accumulated.value를 읽는 순간 폭발합니다 — 누적의 단 하나뿐인 강한 규칙입니다. 하지만 실제 검증에는 의존 규칙이 있습니다("지출에는 양수 금액이 필요하다"). 그래서 폼들은 번번이 수동 if (!acc.hasErrors) 가드로 내려가곤 했습니다. acc.dependent(block)은 그 가드에 이름을 붙인 것입니다. 블록은 지금까지의 모든 브랜치가 성공했을 때만 실행되고 — 그래서 그 안의 형제 .value 읽기는 구조적으로 안전합니다 — 아니면 통째로 건너뜁니다. (Arrow에는 대응물이 없습니다. Arrow 사용자들도 같은 가드를 손으로 굴립니다.)

직접 해 보기

연습: signup의 두 분기를 완성해서 두 번째 호출이 실패를 모두 보고하도록 만들어 보세요.

관련 항목: NonEmptyList — 에러를 실어 나르는 그릇 · Either × 파이프라인fx() 체인 위에서 동시성과 함께 하는 fail-slow 검증 · either & Raise — 이 기능이 확장하는 fail-fast 스코프 · 타입 있는 에러 — 전체 가이드