melatonina
02/17/2020, 9:46 AMsimon.vergauwen
02/17/2020, 9:52 AMTraverse
, which you can use to flip containers. G<F<A>>
to F<G<A>>
in your case Sequence<Option<A>>
to Option<Sequence<A>>
this is a quite common pattern which we can abstract over with Traverse
.
https://arrow-kt.io/docs/arrow/typeclasses/traverse/
It’s also defined directly on SequenceK
.
https://arrow-kt.io/docs/apidocs/arrow-core-data/arrow.core/-sequence-k/
Which you can use with Option.applicative()
and ::identity
or traverse(Option.applicative()) { it }
simon.vergauwen
02/17/2020, 9:54 AMtraverse
the whole sequence
instead of stopping at the first non-null or None
.
It sounds like you just want firstOrNull
but for Option
? What is the reason for using Option
here?melatonina
02/17/2020, 10:01 AMtraverse
https://www.47deg.com/blog/traverse-typeclass-in-arrow/ and I thought "This is related to my problem but not quite what I'm looking for" 😄.simon.vergauwen
02/17/2020, 10:03 AMList
or simple collections and turn it into powerful programs.
val getAllUsers: IO<List<User>> = listOf(1, 2, 3).k().map { getUserById(id) }.sequence(IO.applicative())
melatonina
02/17/2020, 10:03 AMfirstOrNull
for Option
. The reason for using Option is that I may replace it in future with Either
to provide a reason for the failure. It's not something I need now. I can work with a nullable or I can implement my own firstOrNull
for Option
by passing via nullable, and continue my work. I just wanted to exploit this occasion to learn more Λrrow,simon.vergauwen
02/17/2020, 10:04 AMtraverse
for Sequence
will return all results or the failure.melatonina
02/17/2020, 10:06 AMfirstOrNone
for Sequence<Option<T>> then. Thanks!simon.vergauwen
02/17/2020, 10:06 AMfirstOrDefault
in traverse
, I don’t think null
would make sense there if you were using Either
instead of Option
for the nested type.melatonina
02/17/2020, 10:11 AMEither<L, T>
, a behavior that makes sense, in this case, would be reporting why all attempts have failed. So firstOrNone
is good for Sequence<Option<T>>
but not for Sequence<Either<L, T>>
. I'll think about it in future.simon.vergauwen
02/17/2020, 10:12 AMValidated
instead which can have NonEmptyList<E>
in the left side to accumulate all the errors.simon.vergauwen
02/17/2020, 10:12 AMSemigroup
for NonEmptyList
to concatenate all the errors together.melatonina
02/17/2020, 10:13 AMfirstOrNone
for today and I'll refer to this thread when I'll switch to Either<E, T>
.melatonina
02/17/2020, 10:59 AMT?
. firstOrNone
is as simple as
fun <T> Sequence<Option<T>>.firstOrNone() =
firstOrNull { it.nonEmpty() } ?: None
Jannis
02/17/2020, 11:01 AMtraverse
is the wrong abstraction in this case. It uses Applicatives
and thus short-circuits on failure and not success. But there is one that does the exact other thing: Alternative
. It exposes a method called altSum
which is like sequence
that does exactly the same except that it stops as soon as it hits a "success" case. So in your case: firstOrNone == altSum
. Also note that you can always do filterMap(::identity).firstOption()
^^melatonina
02/17/2020, 11:12 AMsimon.vergauwen
02/17/2020, 11:20 AMTraverse
that explains this duality?Jannis
02/17/2020, 11:40 AMTraverse
is also quite interesting, not for this issue, but later on for streaming when using mtl. It's Distributive
and basically does the opposite of Traverse
, instead of "pulling" the inner datatype out it instead "pushes" the outer datatype in^^ Which is quite interesting when you don't want to sequence, but distribute effects^^ (I use the transformer variant in arrow-check to distribute effects over the possibly infinite tree of values a generator provides. That is actually the only correct way to define stateful generators and it's a super power almost no other encoding can achieve^^)Jannis
02/17/2020, 11:41 AMsimon.vergauwen
02/17/2020, 12:16 PM