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

tofu.WithProvide.scala Maven / Gradle / Ivy

package tofu

import cats.~>
import tofu.lift.Lift
import glass.Extract
import tofu.syntax.funk.funK

/** Synonym for [[Provide]] with explicit `C` as `Ctx` and `G` as `Lower` for better type inference
  *
  * Can be seen as transformation `F[_] = C => G[_]`
  */
trait WithProvide[F[_], G[_], C] extends Provide[F] with Lift[G, F] {
  override type Lower[A] = G[A]
  override type Ctx      = C

  /** Runs `fa` providing `ctx` to it.
    *
    * One can treat F as function of type `Ctx => Lower[A]` so this method applies it to `ctx`.
    *
    * @example
    *   Example of usage is to hide sensitive information across a part of service {{{ import tofu.syntax.context._
    *
    * case class UserContext( id: String, phoneNumber: String //very sensitive ) def hidePartOfPhoneNumber: String =>
    * String = ???
    *
    * def contextualLogInfo[F[_]](message: String)(implicit hasUserContext: UserContext In F): F[Unit] = ??? //logs both
    * message AND UserContext
    *
    * def program[F[_]: WithRun[*[_], G[_], UserContext], G[_]: Monad] = for { user <- obtainUserSomehow[G] logUser =
    * contextualLogInfo[F](s"Successfully obtained user") _ <- runContext[F](logUser)(user.toContext) //G[Unit] } yield
    * () }}}
    * @param fa
    *   Contextual computation
    * @return
    *   Result of running fa and providing it context
    */
  override def runContext[A](fa: F[A])(ctx: Ctx): Lower[A]

  /** Same as [[runContext]] but higher-kinded.
    *
    * @example
    *   {{{ trait ProcessHandler[G[_]] { def mapK[M[_]](fk: G ~> M): ProcessHandler[M] = ??? //...other methods } type
    *   WithMyContext[F[_], A] = ReaderT[F, MyCtx, A] val contextualProcessHandler: ProcessHandler[IO WithMyContext _] =
    *   ???
    *
    * val processHandler: ProcessHandler[IO] = processHandler.mapK( WithProvide[IO WithMyContext _, IO,
    * MyCtx].runContextK ) }}}
    */
  override def runContextK(ctx: Ctx): F ~> Lower = funK[F, Lower](runContext(_)(ctx))

  /** Converts context-unaware computation into contextual one by ignoring context
    *
    * One can treat `F[A]` as function of type `Ctx => Lower[A]` so this method creates something like `_ => la` which
    * has type `Ctx => Lower[A]`
    *
    * @return
    */
  def lift[A](la: Lower[A]): F[A]

  /** Allows to focus [[Provide]] on inner parts of its context with lens.
    *
    * @param extract
    *   lens that can extract from `Ctx` value of type `A`
    */
  override def runExtract[A](extract: A Extract Ctx): WithProvide[F, Lower, A] =
    new ProvideExtractInstance[F, Lower, Ctx, A](self, extract)

  def self = this
}

/** Companion object for [[WithProvide]] */
object WithProvide {
  def apply[F[_], G[_], C](implicit p: WithProvide[F, G, C]): WithProvide[F, G, C] = p
}

private[tofu] class ProvideExtractInstance[F[_], G[_], C1, C2](
    ctx: WithProvide[F, G, C1],
    extract: C2 Extract C1
) extends WithProvide[F, G, C2] {
  def runContext[A](fa: F[A])(c: Ctx): G[A] =
    ctx.runContext(fa)(extract.extract(c))

  def lift[A](ga: G[A]): F[A] = ctx.lift(ga)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy