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

tofu.data.Embedded.scala Maven / Gradle / Ivy

package tofu.data

import cats._
import cats.syntax.parallel._
import tofu.syntax.monadic._
import tofu.syntax.feither._
import tofu.syntax.funk
import ExceptTInstances1._
import cats.data.EitherT
import tofu.control.Bind

object Embedded extends ExceptTInstances {
  trait EmbedTag extends Any

  type Base = Any { type EmbedOpaque }
  type T[+F[+_], +G[+_], +A] <: Base with EmbedTag

  def apply[F[+_], G[+_], A](tfa: F[G[A]]): T[F, G, A] = tfa.asInstanceOf[T[F, G, A]]

  implicit final class EmbedOps[F[+_], G[+_], A](private val e: T[F, G, A]) extends AnyVal {
    @inline def value: F[G[A]] = e.asInstanceOf[F[G[A]]]
  }
}

object ExceptT {
  def fromEitherT[F[+_], E, A](fa: EitherT[F, E, A]): ExceptT[F, E, A] = Embedded(fa.value)
}

trait ExceptTInstances extends ExceptTInstances1 {
  implicit def exceptTVeryParallel[G[+_], E](implicit
      G: Parallel[G],
      E: Semigroup[E]
  ): Parallel.Aux[ExceptT[G, E, _], ExceptTPar[G.F, E, _]] =
    new Parallel[ExceptT[G, E, _]] {
      type F[A] = ExceptTPar[G.F, E, A]

      @inline private def par1 = funk.funKFrom[ExceptT[G, E, _]](fx => toPar(G.parallel(fx.value)))

      def parallel   = par1
      def sequential = funk.funKFrom[ExceptTPar[G.F, E, _]](fx => Embedded(G.sequential(fromPar(fx))))

      def monad: Monad[ExceptT[G, E, _]] = exceptTMonad[G, E](G.monad)

      def applicative: Applicative[F] = new ParApplicative[G.F, E]()(G.applicative, E)
    }
}
trait ExceptTInstances1 {
  implicit def exceptTMonad[F[+_], E](implicit F: Monad[F]): MonadError[ExceptT[F, E, _], E] =
    new MonadError[ExceptT[F, E, _], E] {
      override def map[A, B](fa: ExceptT[F, E, A])(f: A => B): ExceptT[F, E, B] =
        Embedded(fa.value.map(_.map(f)))

      def flatMap[A, B](fa: ExceptT[F, E, A])(f: A => ExceptT[F, E, B]): ExceptT[F, E, B] =
        Embedded(fa.value.flatMap {
          case l: Left[E, A] => (l.asInstanceOf[Left[E, B]]).pure[F]
          case Right(a)      => f(a).value
        })

      def tailRecM[A, B](a: A)(f: A => ExceptT[F, E, Either[A, B]]): ExceptT[F, E, B] =
        Embedded(F.tailRecM[A, Either[E, B]](a) { a1 =>
          f(a1).value.map {
            case Left(e)             => Right(Left(e))
            case Right(Left(a))      => Left(a)
            case r @ Right(Right(_)) => r.asInstanceOf[Right[A, Right[E, B]]]
          }
        })

      def pure[A](x: A): ExceptT[F, E, A] = Embedded(Right(x).pure[F])

      override val unit = pure(())

      def handleErrorWith[A](fa: ExceptT[F, E, A])(f: E => ExceptT[F, E, A]): ExceptT[F, E, A] =
        Embedded(fa.value.flatMap {
          case Left(e)        => f(e).value
          case r: Right[E, A] => r.pure[F]
        })

      def raiseError[A](e: E): ExceptT[F, E, A] = Embedded(e.asLeftF[F, A])
    }

  implicit def exceptTBind[F[+_]](implicit F: Monad[F]): Bind[ExceptT[F, _, _]] =
    new Bind[ExceptT[F, _, _]] {
      def pure[E, A](a: A): ExceptT[F, E, A] = Embedded(a.asRightF[F, E])

      def raise[E, A](e: E): ExceptT[F, E, A] = Embedded(e.asLeftF[F, A])

      def foldWith[E, A, X, R](fa: ExceptT[F, E, A])(
          h: E => ExceptT[F, X, R],
          f: A => ExceptT[F, X, R]
      ): ExceptT[F, X, R] = Embedded(fa.value.flatMap {
        case Left(e)  => h(e).value
        case Right(a) => f(a).value
      })

      def foldRec[E, A, X, B](init: Either[E, A])(
          step: Either[E, A] => ExceptT[F, Either[E, X], Either[A, B]]
      ): ExceptT[F, X, B] =
        Embedded {
          type FRR = Either[Either[E, A], Either[X, B]]

          F.tailRecM[Either[E, A], Either[X, B]](init) {
            step(_).value.map {
              case ll @ Left(Left(_))   => ll.asInstanceOf[FRR]
              case Left(Right(x))       => Right(Left(x))
              case Right(Left(a))       => Left(Right(a))
              case rr @ Right(Right(_)) => rr.asInstanceOf[FRR]
            }
          }
        }

    }

  implicit def exceptTParallel[G[+_], E: Semigroup](implicit
      G: Monad[G]
  ): Parallel.Aux[ExceptT[G, E, _], ExceptTPar[G, E, _]] =
    new Parallel[ExceptT[G, E, _]] {
      type F[A] = ExceptTPar[G, E, A]

      def parallel   = funk.funKFrom[ExceptT[G, E, _]](fx => toPar(fx.value))
      def sequential = funk.funK[ExceptTPar[G, E, _], ExceptT[G, E, _]](fx => fromPartoExcept(fx))

      def monad: Monad[ExceptT[G, E, _]] = exceptTMonad[G, E]

      def applicative: Applicative[F] = new ParApplicative[G, E]
    }
}

private[tofu] object ExceptTInstances1 {
  type ExceptTPar[F[_], E, A]
  @inline final def toPar[F[_], E, A](et: F[Either[E, A]])                = et.asInstanceOf[ExceptTPar[F, E, A]]
  @inline final def fromPar[F[_], E, A](et: ExceptTPar[F, E, A])          = et.asInstanceOf[F[Either[E, A]]]
  @inline final def fromPartoExcept[F[+_], E, A](et: ExceptTPar[F, E, A]) = et.asInstanceOf[ExceptT[F, E, A]]

  class ParApplicative[F[_]: Applicative, E: Semigroup] extends Applicative[ExceptTPar[F, E, _]] {
    def pure[A](x: A): ExceptTPar[F, E, A] = toPar(x.asRightF[F, E])

    def ap[A, B](ff: ExceptTPar[F, E, A => B])(fa: ExceptTPar[F, E, A]): ExceptTPar[F, E, B] = map2(ff, fa)(_(_))

    override def map2[A, B, C](fa: ExceptTPar[F, E, A], fb: ExceptTPar[F, E, B])(f: (A, B) => C): ExceptTPar[F, E, C] =
      toPar((fromPar(fa).map2(fromPar(fb))((ea, eb) => (ea, eb).parMapN(f))))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy