All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalaz.std.Future.scala Maven / Gradle / Ivy

The newest version!
package scalaz
package std

import _root_.java.util.concurrent.atomic.AtomicInteger

import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.util.{ Try, Success => TSuccess, Failure => TFailure }
import scala.annotation.tailrec
import scala.util.control.NonFatal

trait FutureInstances1 {
  implicit def futureInstance(implicit ec: ExecutionContext): Nondeterminism[Future] & Cobind[Future] & MonadError[Future, Throwable] & BindRec[Future] =
    new FutureInstance

  implicit def futureSemigroup[A](implicit m: Semigroup[A], ec: ExecutionContext): Semigroup[Future[A]] =
    Semigroup.liftSemigroup[Future, A]
}

private class FutureInstance(implicit ec: ExecutionContext) extends Nondeterminism[Future] with Cobind[Future] with MonadError[Future, Throwable] with BindRec[Future] {
  def point[A](a: => A): Future[A] = Future(a)
  def bind[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa flatMap f
  override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa map f
  def cobind[A, B](fa: Future[A])(f: Future[A] => B): Future[B] = Future(f(fa))
  override def cojoin[A](a: Future[A]): Future[Future[A]] = Future.successful(a)

  def chooseAny[A](head: Future[A], tail: IList[Future[A]]): Future[(A, IList[Future[A]])] = {
    val fs = (head +: tail).zipWithIndex
    val counter = new AtomicInteger(fs.length)
    val result = Promise[(A, Int)]()
    var mutableResult = result
    def attemptComplete(t: Try[(A, Int)]): Unit = {
      val remaining = counter.decrementAndGet
      val result = mutableResult
      if (result != null) {
        t match {
          case TSuccess(_) =>
            val _ = result tryComplete t
            mutableResult = null
          case _ if remaining == 0 =>
            val _ = result tryComplete t
            mutableResult = null
          case _ =>
        }
      }
    }

    fs map { case (fa, i) =>
      fa.onComplete { t => attemptComplete(t.map(_ -> i)) }
    }

    result.future.map { case (a, i) =>
      (a, fs.collect { case (fa, j) if j != i => fa })
    }
  }

  override def firstCompletedOf[A](as: Iterable[Future[A]]): Option[Future[A]] =
    if (as.isEmpty) {
      None
    } else {
      Some(Future.firstCompletedOf(as))
    }

  override def mapBoth[A,B,C](a: Future[A], b: Future[B])(f: (A,B) => C): Future[C] =
    (a zip b).map(f.tupled)

  override def both[A,B](a: Future[A], b: Future[B]): Future[(A,B)] =
    a zip b

  override def gather[A](fs: IList[Future[A]]): Future[IList[A]] =
    sequence(fs)

  // override for actual parallel execution
  override def ap[A, B](fa: => Future[A])(fab: => Future[A => B]) =
    fab zip fa map { case (fa, a) => fa(a) }

  def raiseError[A](e: Throwable): Future[A] =
    Future.failed(e)

  def handleError[A](fa: Future[A])(f: Throwable => Future[A]): Future[A] =
    fa.recoverWith { case e => f(e) }

  def tailrecM[A, B](a: A)(f: A => Future[A \/ B]): Future[B] = {
    @tailrec
    def loop(a: A): Future[B] = {
      val fa =
        try {
          f(a)
        } catch {
          case NonFatal(e) =>
            return Future.failed(e)
        }
      fa.value match {
        case Some(TSuccess(-\/(a))) =>
          loop(a)
        case Some(TSuccess(\/-(b))) =>
          Future.successful(b)
        case Some(TFailure(e)) =>
          Future.failed(e)
        case None =>
          fa.flatMap {
            case \/-(b) =>
              Future.successful(b)
            case -\/(a) =>
              tailrecM(a)(f)
          }
      }
    }
    loop(a)
  }

}

object scalaFuture extends FutureInstances




© 2015 - 2025 Weber Informatics LLC | Privacy Policy