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() } ?: NoneJannis
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