Skip to content

Instantly share code, notes, and snippets.

@LMnet
Created September 30, 2016 04:24
Show Gist options
  • Save LMnet/c3dc91e0c03d163615ff254a80f1dbb0 to your computer and use it in GitHub Desktop.
Save LMnet/c3dc91e0c03d163615ff254a80f1dbb0 to your computer and use it in GitHub Desktop.
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