Is there a reason `Raise.zipOrAccumulate`doesn't h...
# arrow
c
Is there a reason `Raise.zipOrAccumulate`doesn't have a variant which returns a tuple instead of having a combination lambda? I find myself writing
Copy code
fun logIn(username: String, password: String) = either {
    val (validUsername, validPassword) = zipOrAccumulate(
        { withError(LogInFailure::InvalidUsername) { usernameOf(username) } },
        { withError(LogInFailure::InvalidPassword) { passwordOf(password) } },
    ) { u, p -> u to p }

    …further processing…
}
and this overall pattern is quite verbose 😕
k
You should be able to use something like if I’m not mistaken.
Copy code
fun logIn(username: String, password: String) = either {
    val (validUsername, validPassword) = zipOrAccumulate(
        { withError(LogInFailure::InvalidUsername) { usernameOf(username) } },
        { withError(LogInFailure::InvalidPassword) { passwordOf(password) } },
    ::Pair)

    …further processing…
}
👍 1
s
@CLOVIS the problem is resolution.. The compiler cannot distinct between
(A, B, (A,B) -> C) -> C
and
(A, B, C) -> Triple
signature. It confuses these two, and they don't resolve without explicitly "selecting" with like explicit param names, or something. So since it cannot be naturally overloaded we decided not to add
zipOrAccumulateTuple
in favor of
::Triple
.
a
in general, resolution by return type of a function should be avoided as much as possible. Kotlin has a specific
@OverloadByReturnLambda
annotation for this, but in my experience it fails quite often. We considered having versions like
zipOrAccumulatePair
, but at the end of the day these are not more characters than
zipOrAccumulate(...., ..., ::Pair)
s
@Alejandro Serrano.Mena I think one of the biggest problems currently is that
::Triple
doesn't "fit" into
suspend CoroutineScope.(A, B, C) -> D
. (
parMap
, etc. Don't think it's an issue here). It should fit, but an unused extension receivers is currently not automatically ignored. Would that be fixed with
context(CoroutineScope)
? And does that refactor break the binary signature? 🤔 (Sorry, some of these questions only come up naturally 😅) Also, don't think this is about
@OverloadByReturnLambda
since Kotlin cannot understand the difference between
C
and
(A, B) -> C
. Should be unrelated to the lambda return type, right? 🤔
a
we're doing some research in the Kotlin team about how we could make better conversions for callable references, but at this point it seems that everything we do to make something better breaks something elsewhere
s
Awesome to hear! Yes, I have no problem believing that is the case 😰 I'm quite happy with the status quo, and if
context
can be "ignored" than it solves my problem TBH. It fixes it 100% if
context
won't break the binary compat (same ordering of of params), because then it kind-of makes sense that an explicit receiver is not ignored but an "implicit" (?) one can be ignored. Similar train of thought as scope pollution of
buidList
.
c
I wonder if something like this could work:
Copy code
fun logIn(username: String, password: String) = either {
    val (validUsername, validPassword) = Accumulate
        .withError(LogInFailure::InvalidUsername) { usernameOf(username) }
        .withError(LogInFailure::InvalidPassword) { passwordOf(password) }
        .bind()
}
This would be much more convenient to work with for end users because auto-complete would be much less a mess (understanding the signature of the dozen variants of
zipOrAccumulate
is… a challenge). The main downside is it requires creating
n
intermediary result types to have type-safety for the return values 😕 Or maybe
TupleXX
can be reused.
k
What are you trying to accomplish? Less verbosity but with more descriptiveness?
c
Less verbosity, better dev UX with auto-complete