From 998dcf6b5f4a3156595522845ce525134e840be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Andr=C3=A9n?= Date: Thu, 17 Feb 2022 09:34:21 +0100 Subject: [PATCH 1/2] Fix some unused imports --- src/main/scala-2.13/markatta/futiles/Sequencing.scala | 1 - src/main/scala-2.13/markatta/futiles/Traversal.scala | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/scala-2.13/markatta/futiles/Sequencing.scala b/src/main/scala-2.13/markatta/futiles/Sequencing.scala index 0e1b726..d57000b 100644 --- a/src/main/scala-2.13/markatta/futiles/Sequencing.scala +++ b/src/main/scala-2.13/markatta/futiles/Sequencing.scala @@ -18,7 +18,6 @@ package markatta.futiles import scala.collection.BuildFrom import scala.concurrent.{ExecutionContext, Future} -import scala.language.higherKinds import scala.util.Try /** diff --git a/src/main/scala-2.13/markatta/futiles/Traversal.scala b/src/main/scala-2.13/markatta/futiles/Traversal.scala index b7b6737..9393802 100644 --- a/src/main/scala-2.13/markatta/futiles/Traversal.scala +++ b/src/main/scala-2.13/markatta/futiles/Traversal.scala @@ -18,7 +18,6 @@ package markatta.futiles import scala.collection.BuildFrom import scala.concurrent.{ExecutionContext, Future} -import scala.language.higherKinds /** * Utilities that complement scala.concurrent.Future.traverse, working with creating a future out of each element From a0a26da83b5a6a1b22c36b7ddc23e3419b1cc216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Andr=C3=A9n?= Date: Thu, 17 Feb 2022 09:44:51 +0100 Subject: [PATCH 2/2] Add sequencing and traversal for Scala 2.11 --- .../markatta/futiles/Sequencing.scala | 83 +++++++++++++++++++ .../markatta/futiles/Traversal.scala | 49 +++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/main/scala-2.11/markatta/futiles/Sequencing.scala create mode 100644 src/main/scala-2.11/markatta/futiles/Traversal.scala diff --git a/src/main/scala-2.11/markatta/futiles/Sequencing.scala b/src/main/scala-2.11/markatta/futiles/Sequencing.scala new file mode 100644 index 0000000..291e292 --- /dev/null +++ b/src/main/scala-2.11/markatta/futiles/Sequencing.scala @@ -0,0 +1,83 @@ +/* + * Copyright 2015 Johan Andrén + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package markatta.futiles + +import scala.collection.generic.CanBuildFrom +import scala.concurrent.{ExecutionContext, Future} +import scala.language.higherKinds +import scala.util.Try + +/** + * Functions for transforming something with a future inside into a future with something inside + */ +object Sequencing { + + /** + * Turn an option of a future inside out, much like Future.sequence but keep it an Option + */ + def sequenceOpt[A](f: Option[Future[A]]): Future[Option[A]] = + f.map(_.map(Some(_))(CallingThreadExecutionContext)).getOrElse(Future.successful(None)) + + + /** + * @return a Future(Left(l)) for a Left(Future(l)) and a Future(Right(r)) for a Right(Future(r)) + */ + def sequenceEither[L, R](either: Either[Future[L], Future[R]]): Future[Either[L, R]] = + either.fold( + lf => lf.map(l => Left[L, R](l))(CallingThreadExecutionContext), + rf => rf.map(r => Right[L, R](r))(CallingThreadExecutionContext) + ) + + /** + * @return a Future(Left(l)) for a Left(Future(l)) and a Future(Right(r)) for a Right(r) + */ + def sequenceL[L, R](either: Either[Future[L], R]): Future[Either[L, R]] = + sequenceEither(either.right.map(Future.successful)) + + /** + * @return a Future(Left(l)) for a Left(l) and a Future(Right(r)) for a Right(Future(r)) + */ + def sequenceR[L, R](either: Either[L, Future[R]]): Future[Either[L, R]] = + sequenceEither(either.left.map(Future.successful)) + + + /** + * Like Future.sequence() but instead of failing the result future when one future fails + * it will collect each success or failure and complete once all futures has either failed or succeeded + * + * @return A future that completes once all the given futures has completed, successfully or failed, so + * that all of the failures can be handled, reported etc. + * @see Lifting.liftTry() + */ + def sequenceTries[A, M[X] <: TraversableOnce[X]]( + fas: M[Future[A]] + )( + implicit ec: ExecutionContext, cbf: CanBuildFrom[M[Future[A]], Try[A], M[Try[A]]] + ): Future[M[Try[A]]] = { + val fts = fas.foldLeft(Future.successful(cbf())) { (facc, fa) => + for { + acc <- facc + ta <- Lifting.liftTry(fa) + } yield acc += ta + } + fts.map(_.result()) + } + + + + +} diff --git a/src/main/scala-2.11/markatta/futiles/Traversal.scala b/src/main/scala-2.11/markatta/futiles/Traversal.scala new file mode 100644 index 0000000..c5f50eb --- /dev/null +++ b/src/main/scala-2.11/markatta/futiles/Traversal.scala @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Johan Andrén + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package markatta.futiles + +import scala.collection.generic.CanBuildFrom +import scala.concurrent.{ExecutionContext, Future} +import scala.language.higherKinds + +/** + * Utilities that complement scala.concurrent.Future.traverse, working with creating a future out of each element + * in a collection and collecting those into a single future. + */ +object Traversal { + + /** + * For each ```A``` apply the function ```f```, and wait for it to complete before continuing with the next ```A``` + * + * @return The future all ```A```s turned into ```B``` or the first failure that occurred + */ + def traverseSequentially[A, B, M[X] <: TraversableOnce[X]](as: M[A])(f: A => Future[B])(implicit ec: ExecutionContext, cbf: CanBuildFrom[M[A], B, M[B]]): Future[M[B]] = + foldLeftSequentially(as.toTraversable)(cbf())((builder, a) => f(a).map(b => builder += b)).map(_.result()) + + /** + * Like a regular fold left, but with an operation that returns futures, each future will complete before the next + * element in ```as``` is executed. + * + * @param z The zero value, if ```as``` is empty, this is returned, if not this is fed into ```f``` as the ```B``` value + * @return The future of all ```A``` folded into ```B```s, or a future that is failed with any exception that is thrown + * from f + */ + def foldLeftSequentially[A, B](as: Traversable[A])(z: B)(f: (B, A) => Future[B])(implicit ec: ExecutionContext): Future[B] = + if (as.isEmpty) Future.successful(z) + else f(z, as.head).flatMap(b => foldLeftSequentially(as.tail)(b)(f)) + +}