dave08
02/21/2023, 11:18 AMSam
02/21/2023, 11:23 AMdave08
02/21/2023, 11:31 AMbind()
to the happy path and have an aggregation of the failures to pass down or raise.phldavies
02/21/2023, 11:39 AMcontext(Raise<List<L>>)
fun <L, R> Iterable<Either<L, R>>.bind() =
separateEither().let { (errors, success) ->
if(errors.isNotEmpty()) raise(errors) else success
}
phldavies
02/21/2023, 11:42 AMmapOrAccumulate(myListOfEithers) { it.bind() }
dave08
02/21/2023, 11:43 AMdave08
02/21/2023, 11:44 AMdave08
02/21/2023, 11:45 AMphldavies
02/21/2023, 11:53 AMraise
is that it will interrupt the flow (ala railway-oriented programming) which is why raise
returns Nothing
. I think in this case you'll need to stick with separateEither
, process your rights and then manually propagate your lefts back to the outer layer (or provide a callback down into your use-case)dave08
02/21/2023, 11:55 AMval validResults = someIterable.map { service(it) }.onLeft(::callback).bind()
dave08
02/21/2023, 12:00 PMbind()
would only give the success values (w/o having to actually creating two lists)phldavies
02/21/2023, 12:28 PMval validResults = someIterable.mapNotNull { service(it).onLeft(::callback).getOrNull() }
if you're happy with all failures resulting in an empty listphldavies
02/21/2023, 12:33 PMmapNotLeft
has much benefit over mapNotNull { it.getOrNull() }
phldavies
02/21/2023, 12:35 PM._filterIsInstance_<Either.Right<T>>()
dave08
02/21/2023, 12:36 PMdave08
02/21/2023, 12:37 PMdave08
02/21/2023, 12:38 PMmapNotLeftOrAccumulate<ServiceError> { service(it).onLeft(::loggingCallback) }
where ServiceError
is the fatal typedave08
02/21/2023, 12:40 PMphldavies
02/21/2023, 12:42 PMraise
or callback
your errors when checking the result, letting fatal errors get caught by the mapOrAccumulate
and cause the .bind()
to raise, and treat non-fatal errors as an alternative result and filter if required. Something like mapOrAccumulate { service(it).recover { if(it.isFatal) raise(it) else null.also { callback(it) } } }.bind().filterNotNull()
phldavies
02/21/2023, 12:43 PMphldavies
02/21/2023, 12:44 PMdave08
02/21/2023, 12:52 PM"if everything is good, do this with the values, otherwise accumulate and raise all the errors"Oh... so there's no variant of that that wouldn't raise but rather just call a callback... I guess that solution it the way to go then, thanks! I just wonder if I'm not the only one out there who would want some better support for this kind of behaviour in Arrow itself...?
dave08
02/21/2023, 12:53 PMphldavies
02/21/2023, 12:54 PMIor
so can't speak to the semantics of it.phldavies
02/21/2023, 12:55 PMphldavies
02/21/2023, 12:56 PMmapOrAccumulate
or just map
will depend on if you want the first critical failure, or all critical failuresphldavies
02/21/2023, 12:56 PMmapOrAccumulate
is the new validation capability in Arrow 2.x and 1.2.x 😉)simon.vergauwen
02/21/2023, 12:58 PMmapOrAccumulate
& co are already available on the latest alpha and are planned to be released in 1.2.x)dave08
02/21/2023, 12:59 PMmapNotNullOrAccumulate { }
might have been useful for that, but you're right, what you posted DOES cover my use case!
I'm using the latest alpha 😃...phldavies
02/21/2023, 1:00 PM