`Ior.handleErrorWith()` f parameter lacks a Right value
f cannot generate Ior.Right or Ior.Both since it is only passed a leftValue
Needs to be f: (A, B) -> Ior<D, B>
https://github.com/arrow-kt/arrow/blob/8c27809a20c7e8433faf0e844c9c38d00aff9a85/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Ior.kt#L428-L436
But it's meant to handle Left too, so at the very least it'd be something like (A, B?) -> Ior<D, B> no?
Idk.. yes its meant to handle Lefts, but..
at the same time it can generate Rights and for that it needs the inital Right, nullable seems to serve no purpose here - f implementation can just ignore the parameter if it`s not needed
fun f(l, r) {
return Ior.Left(l) // whatever
It doesn't need to generate the right from the initial right though. The type R may be known to the caller, so e.g. something like foo.handleErrorWith(...) { Ior.Right(42) }
yep, makes sense
I mean, I think we can safely add such an overload (overlord resolution will take care of it). I'm tryna think of what the signature of f would be though. Any suggestions? (A, B?) -> Ior<D, B> likely isn't good enough. (A, Option<B>) -> Ior<D, B> maybe?
I don't wanna shut this out completely since maybe it's useful for someone. I think Ior doesn't receive as much love as others and maybe it should. If you're happy though with the current state of affairs then that's good!
Ahh.. I missed this point - we call f from two branches.
When we are in Left one we dont have initial right value so calling f(leftValue) is ok, but when we are in Both we do have right value so the client might expect receiving f(leftValue, rightValue). So logicaly we have here two call variants,
So probably (A, B?) -> Ior<D, B> is a nice fit to indicate with null that we don`t have initial right value.
Also to note the inconsistency in Both branch - where we produce Boths for Left & Both cases, but in Right case stangely enough we simply produce Right.
As I understood the logic for this branch, we enhance type transformed by f with stuffing initial Left/Right in there.
So probably end result should have been Both for all branches?
And final thought: why do we need combine functor at all? If we would have fed initial right & left into f the client could transform them any way he liked, combining and what not. Why overload the interface?
public inline fun <A, B, D> Ior<A, B>.handleErrorWith(f: (A, B?) -> Ior<D, B>): Ior<D, B> {
contract {
callsInPlace(f, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Left -> f(value, null)
is Right -> this
is Both -> f(leftValue, rightValue)
}
}
Seems interesting 🤔
val ior = ...
ior.leftOrNull?.let { left ->
val right = ior.rightOrNull
}
Gives you the same type of info, but without needing to stay within Ior.
public inline fun <A, B, D> Ior<A, B>.handleErrorWith(f: (A, B?) -> Ior<D, B>): Ior<D, B> { contract { callsInPlace(f, InvocationKind.AT_MOST_ONCE) } return when (this) { is Left -> f(value, null) is Right -> this is Both -> f(leftValue, rightValue) } }Seems interesting 🤔
I agree