Hey! Is there something smooth in Arrow that trans...
# arrow
j
Hey! Is there something smooth in Arrow that transforms
List<Either<Error,Success>>
into
Either<Error, List<Success>>
?
j
Ah! Thx! ⭐
s
when in doubt 🧌
w
πŸ€”
s
with that bot, the answer is always
traverse
πŸ˜„
s
Seems like a good point to get some feedback on this πŸ˜„ We're thinking of removing this entirely in favor of the DSL, so
either { map { it.bind() } }
if you're not already within
either { }
and otherwise just
map { it.bind() }
.
In practice you'd typically have
List<Input>
and it would result in something like
input.map { transform(it).bind() }
if you're inside
either { }
.
Where
map { it.bind() }
is
sequence
and
map { transform(it).bind() }
is
traverse
.
s
I would keep it.... nullable types also have
mapNotNull {}
and this is similar πŸ™‚ also
mapNotNull
is basically
map {}.filterNotNull()
but it's common enough
s
Looking at
mapNotNull
makes me think of
mapNotLeft
but that's a horrible name πŸ˜‚ The semantics of
mapNotNull
are quite different though, since it doesn't short-circuit but it filters.
s
ah.... good point πŸ˜„
s
traverse
for
null
!=
mapNotNull
otherwise
mapNotNull
should return
List<A>?
s
anyway... general point still stands.... just because it's easily derived it doesn't mean it shouldn't exist πŸ™‚ especially if it's commonly used but if reducing API surface is improntant... maybe remove just sequence and keep traverse πŸ™‚
s
Keeping it won't impact API surface too much actually... πŸ€” So maybe we should be less drastic with cutting the API. We can switch the implementation to
either { }
without affecting performance and that way it'll be easy to understand on jump to definition. I think flattening
Validated
with
Either
will give most benefit in API surface already. Albeit
traverse
for
Validated
will be now named
mapOrAccumulate
with
bind()
syntax...
Not sure if you've seen those changes @stojan
s
I am aware of
Validated
merging with
Either
, I think I was part of the initial discussion here on slack..... IMHO that's a very good thing.... killing some legacy because of typeclasses πŸ˜„ I am not familiar with the specifics tho πŸ™‚
s
As always I am always looking for feedback 😁 If you have some time, and/or have feedback please let us know. You can find all details on the changes here: β€’ https://github.com/arrow-kt/arrow/pull/2795#issuecomment-1328864003 β€’ Small subsequent PR: https://github.com/arrow-kt/arrow/pull/2892
d
Hi @simon.vergauwen, I know what happens when seeing a
traverse
or
sequence
in the code. If you replace such functions with DSL, I must β€œparse” DSL to determine what the code does. Is it
traverse
or
sequence
, or maybe some custom code? Or someone used an explicit parameter name, and instead of
it
there is a
document
or whatever else. The code should be easy to read. DSL does not help with that. From a newbie’s perspective, the DSL may seem a better solution, as there is less to learn. But for someone with more experience with current Arrow or FP the DSL may be an unnecessary overhead where a single word would suffice. The whole idea of programming is to hide details under a well-named abstraction.
s
Hey @dnowak, Thanks for the feedback ☺️ I think after this small discussion here we will keep
traverse
and
sequence
for
Either
. This feedback is very helpful! Thank you πŸ™
The code should be easy to read. DSL does not help with that.
I don't completely agree with this though. Kotlin offers many things in shape of DSL that tremendously help with readability. For example the usage of smart-casting, but to be fair it's something I initially also had to get used to being familiar with traditional FP style APIs. I think there is a very fine line where DSL drastically improves things, but being too drastic in removing APIs is also counter productive. That's why all this feedback is very crucial to making the right decisions.
Also if you ever see an API being deprecated in 1.x.x do not hesitate to open a discussion here or in the Github issues. It'll be better and easier to remove
@Deprecated
than to break in 2.x.x and re-add it.
k
Can you make intellij suggest traverse for stuff like map { transform(it).bind() } ?
That could help bridge the gap between those that know the "functional dialect" and those that use the dsl
Having a common lexicon is important I think.
s
When K2 and FIR comes out we plan to investigate building Arrow specific analysis and refactoring tools within the IDEA but we're currently waiting for such support to come to the compiler
Additionally, we're hoping that people will use
context(Raise<E>)
instead of
Either
, and that would just make it
map { transform(it) }
without
bind
... which is the far superior pattern, but we'll see how mainstream
context(Raise<E>)
will get. Sticking to old patterns is also not a good thing perse, since FP was not able to get main spread usage that way. I'm giving a talk this weekend about what I think makes FP great in Kotlin. It's also live streamed and recorded. https://events.xebia.com/functional-fun-in-kotlin
k
The use of context(Raise<E>) will depend on the ergonomics of dealing with the effectscope at the edge I think.
s
I love the idea of having one recommend way to do things.... But unfortunately that one way is relying on experimental feature....
s
Can you clarify that a bit? I am not entirely sure what you mean. Do you think its currently not ergonomic?
We're not forcing one way, and both styles don't exclude each other. So there is no need to opt-in for experimental features. We just feel it's quite revolutionary in the way it'll allow FP syntax to be expressed so we have a hard time shutting up about it 🀣
k
I'm not saying it isn't ergonomic as it is, but it introduces a few new concepts
I guess what I'm saying is that we have to show the spring users why this is ergonomic and good πŸ˜„
s
Maybe typealias into an annotation will make spring users happy 🧌
p
imho, we should err on the side of being kotlin idiomatic where possible. Similarly, if we can express the problem/answer in the form of a DSL and through the DSL we can make the problem/solution more accessible(readable, ease-of-use) to everyone, we should continue to invest in DSLs. Also, we should align with the long term vision of the language where possible(e.g., context receivers).
k
Context receivers are pretty stable in their API right? That's what I understood from Elizarov.
s
It's quite hard to predict what will happen, and it'll also differ who you ask. Kotlin is growing a lot, and fast in all direction and with people coming from all kinds of different communities. Front-end (Android vs KMP vs Desktop vs Web), back-end (ktor/Vert.X vs Spring/Quarkus), and much more. I think investing in DSL style, whilst keeping the door open for traditional style gives us best of both worlds. That's why I also said above it'll be a fine line with being too drastic in shedding APIs in favor of DSL or benefitting and that is also why feedback from everyone is crucial.
The API is pretty stable, it's the compiler implementation that is not. They don't want to invest a lot of time fixing all bugs in the current compiler when they'd need to do it all over again in K2.
it's the compiler implementation that is not
What I mean with this is to make it available on Native & JS. That's why it's currently JVM only.
k
Sounds reasonable
I think context(Raise<E>) is a lot easier to introduce to Java devs than monads at least
It's very similar to checked exceptions
270 Views