I have been trying to use more abstract functional programming concepts like those from typelevel/cats for Scala.
In this specific case I am trying to eliminate the need to call map(_.flatten) after traversing some Futures.
Using the standard library it would look something like this:
def stdExample[T](things: Seq[T]): Future[Seq[T]] = {
Future.traverse(things)(futureMaybeThings[T]).map(_.flatten)
}
def futureMaybeThings[T](thing: T): Future[Option[T]] = ???
I tried using flatTraverse from typelevel/cats but the best I could get was this:
def catsExample[T](things: Seq[T]): Future[Seq[T]] = {
things.toList.flatTraverse(futureMaybeThings[T](_).map(_.toList))
}
The call to things.toList is required to get the Traversable, I can live with that one.
Since flatTraverse requires f to be C => G[F[B]] (where both C and B are T, G is Future and F is List), futureMaybeThings doesn't match without first transforming the result with map(_.toList). This ends up being worse than the other solution.
It is possible to create a function that works just on Future and TraversableOnce (since there is an implicit conversion for Option[A] => TraversableOnce[A]. Such an implementation might be like:
def flatTraverse[A, B[_], C, M[X] <: TraversableOnceWithFlatten[X, M]](in: M[A])
(fn: A => Future[B[C]])
(implicit cbf: CanBuildFrom[M[A], B[C], M[B[C]]],
asTraversable: B[C] => TraversableOnce[C],
executor: ExecutionContext): Future[M[C]] = {
Future.traverse(in)(fn).map(_.flatten)
}
This now works how I want:
def customExample[T](things: Seq[T]): Future[Seq[T]] = {
flatTraverse(things)(futureMaybeThings[T])
}
Is there a way to achieve this same thing with typelevel/cats?
Bonus: If not then what would the signature of such a function look like?