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

org.atnos.eff.TimedFuture.scala Maven / Gradle / Ivy

The newest version!
package org.atnos.eff

import java.util.concurrent.TimeoutException
import cats._
import org.atnos.eff.concurrent.Scheduler
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.Promise

final case class TimedFuture[A](callback: (Scheduler, ExecutionContext) => Future[A], timeout: Option[FiniteDuration] = None) {
  @inline def runNow(scheduler: Scheduler, ec: ExecutionContext): Future[A] =
    timeout.fold(callback(scheduler, ec)) { t =>
      val promise = Promise[A]()
      val cancelTimeout = scheduler.schedule({ promise.tryFailure(new TimeoutException); () }, t)
      promise.completeWith(callback(scheduler, ec).map(a => { cancelTimeout(); a })(ec))
      promise.future
    }
}

object TimedFuture {

  final val ApplicativeTimedFuture: Applicative[TimedFuture] = new Applicative[TimedFuture] {
    def pure[A](x: A): TimedFuture[A] =
      TimedFuture((_, _) => Future.successful(x))

    def ap[A, B](ff: TimedFuture[A => B])(fa: TimedFuture[A]): TimedFuture[B] = {
      val newCallback = { (scheduler: Scheduler, ec: ExecutionContext) =>
        val ffRan = ff.runNow(scheduler, ec)
        val faRan = fa.runNow(scheduler, ec)
        faRan.flatMap(a => ffRan.map(f => f(a))(ec))(ec)
      }
      TimedFuture(newCallback)
    }

    override def toString = "Applicative[TimedFuture]"
  }

  implicit final val MonadTimedFuture: MonadError[TimedFuture, Throwable] = new MonadError[TimedFuture, Throwable] {
    def pure[A](x: A): TimedFuture[A] =
      TimedFuture((_, _) => Future.successful(x))

    def flatMap[A, B](fa: TimedFuture[A])(f: A => TimedFuture[B]): TimedFuture[B] =
      TimedFuture[B]((scheduler, ec) => fa.runNow(scheduler, ec).flatMap(f(_).runNow(scheduler, ec))(ec))

    def tailRecM[A, B](a: A)(f: A => TimedFuture[Either[A, B]]): TimedFuture[B] =
      TimedFuture[B] { (scheduler, ec) =>
        def loop(va: A): Future[B] = f(va)
          .runNow(scheduler, ec)
          .flatMap {
            case Left(na) => loop(na)
            case Right(nb) => Future.successful(nb)
          }(ec)
        loop(a)
      }

    def raiseError[A](e: Throwable): TimedFuture[A] =
      TimedFuture((_, _) => Future.failed(e))

    def handleErrorWith[A](fa: TimedFuture[A])(f: Throwable => TimedFuture[A]): TimedFuture[A] =
      TimedFuture((s, ec) => fa.runNow(s, ec).recoverWith[A] { case t => f(t).runNow(s, ec) }(ec))

    override def toString = "MonadError[TimedFuture, Throwable]"
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy