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

org.atnos.eff.addon.monix.TaskEffect.scala Maven / Gradle / Ivy

The newest version!
package org.atnos.eff.addon.monix

import cats._
import cats.effect._
import monix.eval._
import monix.execution._
import org.atnos.eff._
import org.atnos.eff.syntax.eff._
import scala.concurrent.duration.FiniteDuration
import scala.util._

trait TaskTypes {
  type _task[R] = |=[Task, R]
  type _Task[R] = <=[Task, R]
}

trait TaskCreation extends TaskTypes {

  final def fromTask[R: _task, A](task: Task[A], timeout: Option[FiniteDuration] = None): Eff[R, A] =
    timeout.fold(task)(t => task.timeout(t)).send[R]

  final def taskFailed[R: _task, A](t: Throwable): Eff[R, A] =
    fromTask(Task.fromTry[A](Failure(t)))

  final def taskSuspend[R: _task, A](task: => Task[Eff[R, A]], timeout: Option[FiniteDuration] = None): Eff[R, A] =
    fromTask(Task.suspend(task), timeout).flatten

  final def taskDelay[R: _task, A](call: => A, timeout: Option[FiniteDuration] = None): Eff[R, A] =
    fromTask(Task.delay(call), timeout)

  final def taskForkScheduler[R: _task, A](call: Task[A], scheduler: Scheduler, timeout: Option[FiniteDuration] = None): Eff[R, A] =
    fromTask(call.executeOn(scheduler), timeout)

  final def taskFork[R: _task, A](call: Task[A], timeout: Option[FiniteDuration] = None): Eff[R, A] =
    fromTask(call.executeAsync, timeout)

  final def asyncBoundary[R: _task]: Eff[R, Unit] =
    fromTask(forkedUnit)

  final def asyncBoundary[R: _task](s: Scheduler): Eff[R, Unit] =
    fromTask(forkedUnit.executeOn(s))

  private[this] val forkedUnit: Task[Unit] =
    Task.unit.executeAsync

  final def taskAsync[R: _task, A](callbackConsumer: ((Throwable Either A) => Unit) => Unit, timeout: Option[FiniteDuration] = None): Eff[R, A] = {
    val async = Task.create[A] { (_, cb) =>
      callbackConsumer(tea => cb(tea.fold(Failure(_), Success(_))))
      Cancelable.empty
    }
    fromTask(async, timeout)
  }

  def retryUntil[R: _task, A](e: Eff[R, A], condition: A => Boolean, durations: List[FiniteDuration]): Eff[R, A] =
    Eff.retryUntil(e, condition, durations, d => waitFor(d))

  def waitFor[R: _task](duration: FiniteDuration): Eff[R, Unit] =
    Eff.send(Task.deferAction(scheduler => Task.delay { scheduler.scheduleOnce(duration)(()); () }))
}

object TaskCreation extends TaskCreation

trait TaskInterpretation extends TaskTypes {

  private[this] val monixTaskMonad: MonadError[Task, Throwable] =
    MonadError[Task, Throwable]

  private[this] val monixTaskApplicative = new Applicative[Task] {
    override def ap[A, B](ff: Task[A => B])(fa: Task[A]): Task[B] = Task.mapBoth(ff, fa)(_(_))

    override def map2[A, B, Z](fa: Task[A], fb: Task[B])(f: (A, B) => Z): Task[Z] = Task.mapBoth(fa, fb)(f)

    override def product[A, B](fa: Task[A], fb: Task[B]): Task[(A, B)] = Task.mapBoth(fa, fb)((_, _))

    override def pure[A](a: A): Task[A] = Task.now(a)

    override val unit: Task[Unit] = Task.unit

    override def map[A, B](fa: Task[A])(f: A => B): Task[B] = fa.map(f)
  }

  def runAsync[R, A](e: Eff[R, A])(implicit m: Member.Aux[Task, R, NoFx]): Task[A] =
    Eff.detachA(e)(monixTaskMonad, monixTaskApplicative, m)

  def runSequential[R, A](e: Eff[R, A])(implicit m: Member.Aux[Task, R, NoFx]): Task[A] =
    Eff.detach(e)(monixTaskMonad, m)

  import interpret.of

  def taskAttempt[R, A](e: Eff[R, A])(implicit task: Task /= R): Eff[R, Throwable Either A] =
    interpret.interceptNatM[R, Task, Either[Throwable, *], A](
      e,
      new (Task ~> (Task of Either[Throwable, *])#l) {
        def apply[X](fa: Task[X]): Task[Throwable Either X] =
          fa.attempt
      }
    )

  def forkTasks[R, A](e: Eff[R, A])(implicit task: Task /= R): Eff[R, A] =
    interpret.interceptNat[R, Task, A](e)(new (Task ~> Task) {
      def apply[X](fa: Task[X]): Task[X] =
        fa.executeAsync
    })

  /** memoize the task result using a cache */
  def memoize[A](key: AnyRef, cache: Cache, task: Task[A]): Task[A] =
    Task.suspend {
      cache.get[A](key).fold(task.map { r => cache.put(key, r); r })(Task.now)
    }

  /**
    * Memoize task effects using a cache
    *
    * if this method is called with the same key the previous value will be returned
    */
  def taskMemo[R, A](key: AnyRef, cache: Cache, e: Eff[R, A])(implicit task: Task /= R): Eff[R, A] =
    taskAttempt(Eff.memoizeEffect(e, cache, key)).flatMap {
      case Left(t) => Eff.send(taskSequenceCached.reset(cache, key)) >> TaskEffect.taskFailed(t)
      case Right(a) => Eff.pure(a)
    }

  implicit val taskSequenceCached: SequenceCached[Task] = new SequenceCached[Task] {
    def get[X](cache: Cache, key: AnyRef): Task[Option[X]] =
      Task.delay(cache.get(key)).executeAsync

    def apply[X](cache: Cache, key: AnyRef, sequenceKey: Int, tx: => Task[X]): Task[X] =
      cache.memo((key, sequenceKey), tx.memoize)

    def reset(cache: Cache, key: AnyRef): Task[Unit] =
      Task.delay {
        cache.reset(key)
        var i = 0
        while (cache.get((key, i)).isDefined) {
          cache.reset((key, i))
          i += 1
        }
      }
  }

}

object TaskInterpretation extends TaskInterpretation

trait EffToTask[R] {
  def apply[A](e: Eff[R, A]): Task[A]
}

trait TaskEffect extends TaskInterpretation with TaskCreation { outer =>

  implicit def asyncInstance[R: _Task](implicit runEff: EffToTask[R]): cats.effect.Async[Eff[R, *]] = new cats.effect.Async[Eff[R, *]] {
    private[this] val taskAsyncInstance: cats.effect.Async[Task] =
      implicitly[cats.effect.Async[Task]]

    override def asyncF[A](k: (Either[Throwable, A] => Unit) => Eff[R, Unit]): Eff[R, A] = fromTask(taskAsyncInstance.asyncF[A] { f => runEff(k(f)) })

    override def bracketCase[A, B](acquire: Eff[R, A])(use: A => Eff[R, B])(release: (A, ExitCase[Throwable]) => Eff[R, Unit]): Eff[R, B] =
      fromTask(taskAsyncInstance.bracketCase(runEff(acquire))(a => runEff(use(a)))((r, ec) => runEff(release(r, ec))))

    def async[A](k: (Either[Throwable, A] => Unit) => Unit): Eff[R, A] =
      fromTask(taskAsyncInstance.async(k))

    def suspend[A](thunk: => Eff[R, A]): Eff[R, A] =
      fromTask(Task.apply(thunk)).flatten

    def raiseError[A](e: Throwable): Eff[R, A] =
      fromTask(taskAsyncInstance.raiseError(e))

    def handleErrorWith[A](fa: Eff[R, A])(f: Throwable => Eff[R, A]): Eff[R, A] =
      taskAttempt(fa).flatMap {
        case Left(t) => f(t)
        case Right(a) => Eff.pure(a)
      }

    def pure[A](a: A): Eff[R, A] =
      Eff.pure(a)

    def flatMap[A, B](fa: Eff[R, A])(f: A => Eff[R, B]): Eff[R, B] =
      fa.flatMap(f)

    def tailRecM[A, B](a: A)(f: A => Eff[R, Either[A, B]]): Eff[R, B] =
      Eff.EffMonad[R].tailRecM(a)(f)

  }

  def effectInstance[R: _Task](implicit runEff: EffToTask[R], scheduler: Scheduler): cats.effect.Effect[Eff[R, *]] =
    new cats.effect.Effect[Eff[R, *]] {

      private[this] val taskEffectInstance: cats.effect.Effect[Task] =
        implicitly[cats.effect.Effect[Task]]

      private[this] val asyncInstance: cats.effect.Async[Eff[R, *]] =
        outer.asyncInstance

      override def asyncF[A](k: (Either[Throwable, A] => Unit) => Eff[R, Unit]) = asyncInstance.asyncF(k)

      override def bracketCase[A, B](acquire: Eff[R, A])(use: A => Eff[R, B])(release: (A, ExitCase[Throwable]) => Eff[R, Unit]) =
        asyncInstance.bracketCase(acquire)(use)(release)

      def runAsync[A](fa: Eff[R, A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit] =
        taskEffectInstance.runAsync(runEff(fa))(cb)

      def async[A](k: (Either[Throwable, A] => Unit) => Unit): Eff[R, A] =
        asyncInstance.async(k)

      def suspend[A](thunk: => Eff[R, A]): Eff[R, A] =
        asyncInstance.defer(thunk)

      def raiseError[A](e: Throwable): Eff[R, A] =
        asyncInstance.raiseError(e)

      def handleErrorWith[A](fa: Eff[R, A])(f: Throwable => Eff[R, A]): Eff[R, A] =
        asyncInstance.handleErrorWith(fa)(f)

      def pure[A](a: A): Eff[R, A] =
        Eff.pure(a)

      def flatMap[A, B](fa: Eff[R, A])(f: A => Eff[R, B]): Eff[R, B] =
        fa.flatMap(f)

      def tailRecM[A, B](a: A)(f: A => Eff[R, Either[A, B]]): Eff[R, B] =
        Eff.EffMonad[R].tailRecM(a)(f)
    }

}

object TaskEffect extends TaskEffect




© 2015 - 2024 Weber Informatics LLC | Privacy Policy