The PR which proposes probably the biggest change ...
# arrow
s
The PR which proposes probably the biggest change from 1.x.x to 2.x.x, removing
Validated
and integrating its functionality into
Either
. https://github.com/arrow-kt/arrow/pull/2795#issuecomment-1328864003 Feedback, and thoughts are very welcome 🙏
👀 4
❤️ 1
👍 1
s
any thoughts on top level
zip
function instead of the current extension function? I think it existed in earlier versions of arrow but was replaced by extension functions at one point e.g.
zip
and
combine
from kotlinx coroutines are top level functions
s
You mean
zip(fa, fb, fc) { a, b, c -> }
instead of
fa.zip(fb, fc) { a, b, c -> }
?
Curious because in Kotlin Std there are a couple of functions that are extension functions
zip
. I.e. on
Iterable<A>
s
yeah, but AFIK for
Iterable
example, it's always two of them that are zipped... so extension function makes sense there IMO but when we have them with arity 2-7, top level makes more sense IMO
s
I'm not sure I follow that train of thought 🤔 I wish there were guidelines for this, or that the Jetbrains people would document why these functions are shaped differently in Kotlin Std and KotlinX.
Huh,
combine
is defined as an extension for arity-2 and for arity 3+ it's top-level 🤯 https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/combine.html
The only other top-level are
vararg
which is different, since that doesn't allow independent functions like
Either#zip
.
s
interesting... didn't notice arity 2 was an extension function... I guess I've always needed more than 2 :D
I wish there were guidelines for this, or that the Jetbrains people would document why these functions are shaped differently in Kotlin Std and KotlinX.
maybe we can ask in the #stdlib channel?
s
It's strange to me that you have to rewrite
flowA.zip(flowB) { a, b -> }
to
zip(flowA, flowB, flowC) { a, b, c -> }
s
good point, not very refactor friendly for adding an argument
p
I found the
Validated<A>.zip(b: V<B>, c: V<C>, (A, B, C) -> R)
odd that it feels like it elevates
A
other the others as different. If you want to rearrange the arguments to the block you end up swapping receivers around.
s
I'm not sure I follow @phldavies. Could you give an example?
p
For example (contrived)
Copy code
val username = fields.required("username").andThen(String::parseUsername)
val email = fields.required("email").andThen(String::parseEmail)
val age = fields.required("age").andThen(String::parseAge)

email.zip(username, age, ::CreateUserRequest)
Why is
email
different to
username
or
age
in this case? If I wanted to updated
CreateUserRequest
to take
username
first, I’d need to update the receiver of
zip
rather than just the argument ordering. It just feels like the first argument being bound is treated specially. For this reason, I sometimes find myself adding a top-level
zip(A, B, C, (a, b, c) -> R)
helper to make it feel a little more natural. As an example, kotest has a top-level
Arb.bind(Gen<A>, Gen<B>, Gen<C>, (a, b, c) -> R) -> Arb<R>
rather than a
Gen<A>.zip(...)
s
Okay, thank you I understand now so
Either.zip(fa, fb, fc) { a, b, c -> }
would make more sense to you? I guess this is similar to what @stojan was saying. One of the original reasoning, besides getting inspiration from
Iterable<A>#zip
was because
fa.zip
is easier to find through auto-completion.
p
At least for the use-case of
Validated
I initially looked for an extension off the
Validated
companion, iirc. I can certainly see your reasoning though, given stdlib provides
Iterable<A>.zip(Iterable<B>, (A, B) -> C)
and arrow extends that with n-arity forms with the receiver maintained.
s
Yes, I can see where you're coming from. In FP it's typically defined
mapN(fa, fb, fc) { a, b, c -> }
or with special syntax
fa @ fb @ fc
(returning a Tuple).
s
I initially looked for an extension off the
Validated
companion
AFIK the companion object strategy is not used in the standard lib or kotlinx coroutines, and the kotlin team suggests top level functions instead