Created
September 30, 2016 04:24
-
-
Save LMnet/c3dc91e0c03d163615ff254a80f1dbb0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.concurrent.{ExecutionContext, Future} | |
/** | |
* Takes care of computations | |
* | |
* Success(either) - the computation will be continued. | |
* Failure(error) - the computation was failed with unhandled error. | |
* | |
* Either[Result, T]: | |
* Left(result) is a final and handled result, another computations (map, flatMap) will be ignored. | |
* Right(T) is a current result. Functions in map/flatMap will continue the computation. | |
* | |
* Example: | |
* {{{ | |
* import scala.concurrent.ExecutionContext.Implicits.global | |
* import scala.concurrent.{ExecutionContext, Future} | |
* import com.drivergrp.rep.server.utils.Computation | |
* | |
* def successful = for { | |
* x <- Computation.continue(1) | |
* y <- Computation.continue(2) | |
* } yield s"$x + $y" | |
* | |
* // Prints "Success(1 + 2)" | |
* successful.join.onComplete(println) | |
* | |
* def failed = for { | |
* x <- Computation.abort("Failed on x") | |
* _ = println("Second step") | |
* y <- Computation.continue(2) | |
* } yield s"$x + $y" | |
* | |
* // Prints "Success(Failed on x)" | |
* failed.join.onComplete(println) | |
* }}} | |
* | |
* @param future The final flow in a future. | |
* @tparam R Type of result for aborted computation. | |
* @tparam T Type of result for continued computation. | |
*/ | |
final case class Computation[+R, +T](future: Future[Either[R, T]]) { | |
def flatMap[R2, T2](f: T => Computation[R2, T2])(implicit ec: ExecutionContext, ev: R <:< R2): Computation[R2, T2] = { | |
Computation(future.flatMap { | |
case Left(x) => Future.successful(Left(x)) | |
case Right(x) => f(x).future | |
}) | |
} | |
def map[T2](f: T => T2)(implicit ec: ExecutionContext): Computation[R, T2] = flatMap { a => | |
Computation.continue(f(a)) | |
} | |
def filter(f: T => Boolean)(implicit ec: ExecutionContext): Computation[R, T] = map { a => | |
if (f(a)) a | |
else throw new NoSuchElementException("When filtering") | |
} | |
def withFilter(f: T => Boolean)(implicit ec: ExecutionContext): Computation[R, T] = filter(f) | |
def foreach[T2](f: T => T2)(implicit ec: ExecutionContext): Unit = future.foreach { | |
case Right(x) => f(x) | |
case _ => | |
} | |
def toFuture[R2](resultFormatter: T => R2)(implicit ec: ExecutionContext, ev: R <:< R2): Future[R2] = future.map { | |
case Left(x) => x | |
case Right(x) => resultFormatter(x) | |
} | |
def toFuture[R2](implicit ec: ExecutionContext, ev1: R <:< R2, ev2: T <:< R2): Future[R2] = future.map { | |
case Left(x) => x | |
case Right(x) => x | |
} | |
} | |
object Computation { | |
def continue[T](x: T): Computation[Nothing, T] = Computation(Future.successful(Right(x))) | |
def abort[R](result: R): Computation[R, Nothing] = Computation(Future.successful(Left(result))) | |
def fail(exception: Throwable): Computation[Nothing, Nothing] = Computation(Future.failed(exception)) | |
def fromFuture[T](input: Future[T])(implicit ec: ExecutionContext): Computation[Nothing, T] = Computation { | |
input.map { x => Right(x) } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment