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

korolev.effect.Effect.scala Maven / Gradle / Ivy

/*
 * Copyright 2017-2020 Aleksey Fomkin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package korolev.effect

import korolev.effect.Effect.Fiber

import scala.annotation.implicitNotFound
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future, blocking => futureBlocking}
import scala.util.{Failure, Success, Try}

/**
  * Korolev's internal presentation of effect (such as Future, cats.effect.IO, Monix or ZIO tasks).
  * Contains enough functionality to make Korolev works.
  */
@implicitNotFound("Instance of Effect for ${F} is not found.")
trait Effect[F[_]] {
  private val noneVal: F[None.type] = pure(None)
  def none[A]: F[Option[A]] = noneVal.asInstanceOf[F[Option[A]]]
  def pure[A](value: A): F[A]
  def delay[A](value: => A): F[A]
  def delayAsync[A](value: => F[A]): F[A] = flatMap(delay(value))(identity)
  def fail[A](e: Throwable): F[A]
  def unit: F[Unit]
  def never[T]: F[T]
  def fromTry[A](value: => Try[A]): F[A]
  def promise[A](cb: (Either[Throwable, A] => Unit) => Unit): F[A]
  def promiseF[A](cb: (Either[Throwable, A] => Unit) => F[Unit]): F[A]
  def flatMap[A, B](m: F[A])(f: A => F[B]): F[B]
  def map[A, B](m: F[A])(f: A => B): F[B]
  def recover[A, AA >: A](m: F[A])(f: PartialFunction[Throwable, AA]): F[AA]
  def recoverF[A, AA >: A](m: F[A])(f: PartialFunction[Throwable, F[AA]]): F[AA]
//  def onError[A](m: F[A])(f: Throwable => Unit): F[A]
//  def onErrorF[A](m: F[A])(f: Throwable => F[Unit]): F[A]
  /** Keep in mind that when [[F]] has strict semantic, effect should
    * created inside 'start()' brackets. */
  def start[A](create: => F[A])(implicit ec: ExecutionContext): F[Fiber[F, A]]
  def blocking[T](f: => T)(implicit ec: ExecutionContext): F[T]

  /** Keep in mind that when [[F]] has strict semantic, effect should
    * created inside 'fork()' brackets. */
  def fork[A](m: => F[A])(implicit ec: ExecutionContext): F[A]
  def sequence[A](in: List[F[A]]): F[List[A]]
  def runAsync[A](m: F[A])(callback: Either[Throwable, A] => Unit): Unit
  def run[A](m: F[A]): Either[Throwable, A]
  def toFuture[A](m: F[A]): Future[A]
}

object Effect {

  type Promise[A] = Either[Throwable, A] => Unit

  trait Fiber[F[_], A] {
    def join(): F[A]
  }

  def apply[F[_]: Effect]: Effect[F] = implicitly[Effect[F]]

  class FutureEffect extends Effect[Future] {
    private implicit val immediateEc: ExecutionContext = new ExecutionContext {
      // Run on the same thread
      def execute(runnable: Runnable): Unit = runnable.run()
      def reportFailure(cause: Throwable): Unit = cause.printStackTrace()
    }
    val unit: Future[Unit] = Future.unit
    def never[T]: Future[T] = Future.never
    def toFuture[A](m: Future[A]): Future[A] = m
    def fail[A](e: Throwable): Future[A] = Future.failed(e)
    def pure[A](value: A): Future[A] = Future.successful(value)
    def delay[A](value: => A): Future[A] =
      try {
        Future.successful(value)
      } catch {
        case error: Throwable =>
          Future.failed(error)
      }
    def fork[A](m: => Future[A])(implicit ec: ExecutionContext): Future[A] =
      Future(m)(ec).flatten
    def fromTry[A](value: => Try[A]): Future[A] = Future.fromTry(value)
    def flatMap[A, B](m: Future[A])(f: A => Future[B]): Future[B] = m.flatMap(f)
    def map[A, B](m: Future[A])(f: A => B): Future[B] = m.map(f)
    def runAsync[A](m: Future[A])(f: Either[Throwable, A] => Unit): Unit =
      m.onComplete(x => f(x.toEither))
    def run[A](m: Future[A]): Either[Throwable, A] =
      Try(Await.result(m, Duration.Inf)).toEither
    def recover[A, AA >: A](m: Future[A])(f: PartialFunction[Throwable, AA]): Future[AA] = m.recover(f)
    def recoverF[A, AA >: A](m: Future[A])(f: PartialFunction[Throwable, Future[AA]]): Future[AA] = m.recoverWith(f)
//    def onError[A](m: Future[A])(f: Throwable => Unit): Future[A] = {
//      m.onComplete {
//        case Success(value) => ()
//        case Failure(exception) =>
//          f(exception)
//      }
//      m
//    }
//    def onErrorF[A](m: Future[A])(f: Throwable => Future[Unit]): Future[A] = {
//      m.onComplete {
//        case Success(value) => ()
//        case Failure(exception) =>
//          f(exception)
//      }
//      m
//    }
    /** Keep in mind that when [[F]] has strict semantic, effect should
      * created inside 'start()' brackets. */
    def sequence[A](in: List[Future[A]]): Future[List[A]] =
      Future.sequence(in)
    def start[A](create: => Future[A])(implicit ec: ExecutionContext): Future[Fiber[Future, A]] = {
      val f = Future(create)(ec).flatten
      Future.successful {
        new Fiber[Future, A] {
          override def join(): Future[A] = f
        }
      }
    }
    def promise[A](cb: (Either[Throwable, A] => Unit) => Unit): Future[A] = {
      val promise = scala.concurrent.Promise[A]()
      try {
        cb(or => promise.complete(or.toTry))
        promise.future
      } catch {
        case e: Throwable =>
          Future.failed(e)
      }
    }
    def promiseF[A](cb: (Either[Throwable, A] => Unit) => Future[Unit]): Future[A] = {
      val promise = scala.concurrent.Promise[A]()
      // FIXME handle error
      cb(or => promise.complete(or.toTry)).flatMap { _ =>
        promise.future
      }
    }
    def blocking[T](f: => T)(implicit ec: ExecutionContext): Future[T] =
      Future(futureBlocking(f))(ec)
  }

  implicit val futureEffect: Effect[Future] =
    new FutureEffect()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy