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

caliban.interop.cats.FromEffect.scala Maven / Gradle / Ivy

The newest version!
package caliban.interop.cats

import cats.effect.std.Dispatcher
import cats.~>
import zio.{ RIO, Tag, ZEnvironment, ZIO }

/**
 * Describes how [[zio.RIO]] can be created from a polymorphic effect `F`.
 *
 * @tparam F the higher-kinded type of a polymorphic effect
 * @tparam R the type of ZIO environment
 */
@annotation.implicitNotFound("""
Could not find `FromEffect` for effect ${F} and environment ${R}. `FromEffect` can be one of the following:

1) Non-contextual: derived automatically when `cats.effect.std.Dispatcher` is available in the implicit scope. A way to go for non-contextual effects (e.g. `cats.effect.IO`):

implicit val dispatcher: Dispatcher[${F}] = ???

val fromEffect: FromEffect[${F}, ${R}] = implicitly // or an explicit call `FromEffect.forDispatcher`

2) Contextual: injects ZIO environment into underlying effect. Can be used to share a context between ZIO and Kleisli-like effects:

case class Context(isAdmin: Boolean)
type Effect[A] = Kleisli[IO, Context, A]

val dispatcher: Dispatcher[Effect] = ???

implicit val injectContext: InjectEnv[Effect, Context] = InjectEnv.kleisli
implicit val fromEffect: FromEffect[Effect, Context] = FromEffect.contextual(dispatcher)

""")
trait FromEffect[F[_], R] {
  def fromEffect[A](fa: F[A]): RIO[R, A]

  final lazy val fromEffectK: F ~> RIO[R, *] =
    new (F ~> RIO[R, *]) {
      def apply[A](fa: F[A]): RIO[R, A] = fromEffect(fa)
    }

}

/**
 * @define contextualConversion
 *         Contextual conversion from a polymorphic effect `F` to [[zio.RIO]].
 *
 *         An environment of type `R` is injected into the effect `F` via `injector`.
 *         The execution of `RIO[R, A]` relies on the environment `R` modified by [[InjectEnv.modify]].
 *
 *         @see See [[InjectEnv]] for more details about injection.
 *
 * @define dispatcherParam the instance of [[cats.effect.std.Dispatcher]]. Required in order to perform the conversion
 *
 * @define injectorParam injects the given environment of type `R` into the effect `F`
 *
 * @define fParam the higher-kinded type of a polymorphic effect
 *
 * @define rParam the type of ZIO environment
 */
object FromEffect {

  /**
   * Contextual version of the [[FromEffect]].
   *
   * @tparam F the higher-kinded type of a polymorphic effect
   * @tparam R the type of ZIO environment
   */
  trait Contextual[F[_], R] extends FromEffect[F, R] {
    def fromEffect[A](fa: F[A], env: ZEnvironment[R]): RIO[R, A]

    final def fromEffect[A](fa: F[A]): RIO[R, A] =
      for {
        env    <- ZIO.environment[R]
        result <- fromEffect(fa, env)
      } yield result
  }

  def apply[F[_], R](implicit ev: FromEffect[F, R]): FromEffect[F, R] = ev

  /**
   * $contextualConversion
   *
   * @param dispatcher $dispatcherParam
   * @param injector $injectorParam
   * @tparam F $fParam
   * @tparam R $rParam
   */
  def contextual[F[_], R: Tag](dispatcher: Dispatcher[F])(implicit
    injector: InjectEnv[F, R]
  ): FromEffect.Contextual[F, R] =
    contextual(forDispatcher[F, R](dispatcher))

  /**
   * $contextualConversion
   *
   * @param from the underlying conversion from `F` to [[zio.RIO]]
   * @param injector $injectorParam
   * @tparam F $fParam
   * @tparam R $rParam
   */
  def contextual[F[_], R: Tag](
    from: FromEffect[F, R]
  )(implicit injector: InjectEnv[F, R]): FromEffect.Contextual[F, R] =
    new FromEffect.Contextual[F, R] {
      def fromEffect[A](fa: F[A], env: ZEnvironment[R]): RIO[R, A] =
        from.fromEffect(injector.inject(fa, env.get))
    }

  /**
   * Default (non-contextual) conversion from a polymorphic effect `F` to [[zio.RIO]].
   *
   * Identical to what [[https://github.com/zio/interop-cats]] offers.
   *
   * @param dispatcher $dispatcherParam
   * @tparam F $fParam
   * @tparam R $rParam
   */
  implicit def forDispatcher[F[_], R](implicit dispatcher: Dispatcher[F]): FromEffect[F, R] =
    new FromEffect[F, R] {
      def fromEffect[A](fa: F[A]): RIO[R, A] =
        ZIO
          .succeed(dispatcher.unsafeToFutureCancelable(fa))
          .flatMap { case (future, cancel) =>
            ZIO.fromFuture(_ => future).onInterrupt(ZIO.fromFuture(_ => cancel()).orDie).interruptible
          }
          .uninterruptible
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy