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

tofu.data.calc.CalcMInstances.scala Maven / Gradle / Ivy

package tofu.data.calc

import cats.{Applicative, Bifoldable, Bitraverse, Eval, Functor, MonadError, StackSafeMonad}
import tofu.WithRun
import tofu.bi.BiRun
import tofu.control.{Bind, StackSafeBind}
import tofu.data.calc.StepResult._
import tofu.higherKind.bi.FunBK
import tofu.syntax.monadic._

trait CalcMInstances extends CalcMInstances1 {
  final implicit def calcFunctorInstance[F[+_, +_], R, S, E]: CalcMonadInstance[F, R, S, E] =
    new CalcMonadInstance[F, R, S, E]

  final implicit def calcBindInstance[F[+_, +_], R, S]: CalcBindInstance[F, R, S] =
    new CalcBindInstance[F, R, S]

  final implicit def calcContextInstance[F[+_, +_], R, S, E]: CalcContextInstance[F, R, S, E] =
    new CalcContextInstance[F, R, S, E]

  final implicit def calcBiContextInstance[F[+_, +_], R, S]: CalcBiContextInstance[F, R, S] =
    new CalcBiContextInstance[F, R, S]

}

trait CalcMInstances1 {
  final implicit def calcMBitraverse[F[+_, +_]: Bitraverse, S]: Bitraverse[CalcM[F, Any, Any, S, _, _]] =
    new CalcMBitraverse
}

trait CalcMInstances2 {
  final implicit def calcMBifoldable[F[+_, +_]: Bifoldable, S]: Bifoldable[CalcM[F, Any, Any, S, _, _]] =
    new CalcMBifoldable
}

class CalcMonadInstance[F[+_, +_], R, S, E]
    extends MonadError[CalcM[F, R, S, S, E, _], E] with cats.Defer[CalcM[F, R, S, S, E, _]]
    with StackSafeMonad[CalcM[F, R, S, S, E, _]] {
  def defer[A](fa: => CalcM[F, R, S, S, E, A]): CalcM[F, R, S, S, E, A]                                         = CalcM.defer(fa)
  def raiseError[A](e: E): CalcM[F, R, S, S, E, A]                                                              = CalcM.raise(e)
  def handleErrorWith[A](fa: CalcM[F, R, S, S, E, A])(f: E => CalcM[F, R, S, S, E, A]): CalcM[F, R, S, S, E, A] =
    fa.handleWith(f)
  def flatMap[A, B](fa: CalcM[F, R, S, S, E, A])(f: A => CalcM[F, R, S, S, E, B]): CalcM[F, R, S, S, E, B]      =
    fa.flatMap(f)
  def pure[A](x: A): CalcM[F, R, S, S, E, A]                                                                    = CalcM.pure(x)
}

class CalcBindInstance[F[+_, +_], R, S] extends StackSafeBind[CalcM[F, R, S, S, _, _]] {
  override def pure[E, A](a: A): CalcM[F, R, S, S, E, A] = CalcM.pure(a)

  override def raise[E, A](e: E): CalcM[F, R, S, S, E, A] = CalcM.raise(e)

  override def foldWith[E, A, X, B](fa: CalcM[F, R, S, S, E, A])(
      h: E => CalcM[F, R, S, S, X, B],
      f: A => CalcM[F, R, S, S, X, B]
  ): CalcM[F, R, S, S, X, B] = fa.foldWith(f, h)
}

class CalcContextInstance[F[+_, +_], R, S, E] extends WithRun[CalcM[F, R, S, S, E, _], CalcM[F, Any, S, S, E, _], R] {
  override val context: CalcM[F, R, S, S, E, R]          = CalcM.read
  override val functor: Functor[CalcM[F, R, S, S, E, _]] = CalcM.calcFunctorInstance

  override def runContext[A](fa: CalcM[F, R, S, S, E, A])(ctx: R): CalcM[F, Any, S, S, E, A]   = fa.provide(ctx)
  override def local[A](fa: CalcM[F, R, S, S, E, A])(project: R => R): CalcM[F, R, S, S, E, A] =
    fa.provideSome(project)
  override def lift[A](fa: CalcM[F, Any, S, S, E, A]): CalcM[F, R, S, S, E, A]                 = fa
}

class CalcBiContextInstance[F[+_, +_], R, S]
    extends BiRun[CalcM[F, R, S, S, +_, +_], CalcM[F, Any, S, S, +_, +_], Nothing, R] {
  override def bifunctor: Bind[CalcM[F, R, S, S, _, _]] = CalcM.calcBindInstance

  override def lift[E, A](fa: CalcM[F, Any, S, S, E, A]): CalcM[F, R, S, S, E, A] = fa

  override def runLeft[E, A](fa: CalcM[F, R, S, S, E, A])(x: Nothing): CalcM[F, Any, S, S, E, A] = x

  override def runRight[E, A](fa: CalcM[F, R, S, S, E, A])(r: R): CalcM[F, Any, S, S, E, A] = fa.provide(r)

  override def context: CalcM[F, R, S, S, Nothing, R] = CalcM.read

  override def bilocal[E, A](
      fea: CalcM[F, R, S, S, E, A]
  )(lproj: Nothing => Nothing, rproj: R => R): CalcM[F, R, S, S, E, A] = fea.local(rproj)

  override def disclose[E, A](
      k: FunBK[CalcM[F, R, S, S, _, _], CalcM[F, Any, S, S, _, _]] => CalcM[F, R, S, S, E, A]
  ): CalcM[F, R, S, S, E, A] = CalcM.read[S, R].flatMap(r => k(FunBK.apply[CalcM[F, R, S, S, _, _]](_.provide(r))))
}

class CalcMBifoldable[F[+_, +_], S](implicit F: Bifoldable[F]) extends Bifoldable[CalcM[F, Any, Any, S, _, _]] {
  private[this] val MaxDepth = 256

  def bifoldLeft1Slow[E, A, B](
      c: StepResult[F, S, E, A],
      b: Eval[B]
  )(f: (Eval[B], E) => Eval[B], g: (Eval[B], A) => Eval[B]): Eval[B] =
    Eval.defer(c match {
      case Error(_, e)                     => f(b, e)
      case Ok(_, a)                        => g(b, a)
      case w: Wrap[F, r, s, S, x, E, m, A] =>
        F.bifoldLeft(w.inner, b)(
          (b1, x) => bifoldLeft1Slow(w.stepFailure(x), b1)(f, g),
          (b1, m) => bifoldLeft1Slow(w.stepSuccess(m), b1)(f, g)
        )
    })

  def bifoldLeft1[E, A, B](c: StepResult[F, S, E, A], b: B, depth: Int)(f: (B, E) => B, g: (B, A) => B): B = {
    if (depth == MaxDepth)
      bifoldLeft1Slow(c, Eval.now(b))(
        (eb, e) => eb.map(f(_, e)),
        (eb, a) => eb.map(g(_, a))
      ).value
    else
      c match {
        case Error(_, e)                     => f(b, e)
        case Ok(_, a)                        => g(b, a)
        case w: Wrap[F, r, s, S, x, E, m, A] =>
          F.bifoldLeft(w.inner, b)(
            (b1, x) => bifoldLeft1(w.stepFailure(x), b1, depth + 1)(f, g),
            (b1, m) => bifoldLeft1(w.stepSuccess(m), b1, depth + 1)(f, g)
          )
      }
  }

  def bifoldLeft[A, B, C](fab: CalcM[F, Any, Any, S, A, B], c: C)(f: (C, A) => C, g: (C, B) => C): C =
    bifoldLeft1(fab.step((), ()), c, 0)(f, g)

  def bifoldRight1[E, A, B](c: StepResult[F, S, E, A], b: Eval[B])(
      f: (E, Eval[B]) => Eval[B],
      g: (A, Eval[B]) => Eval[B]
  ): Eval[B] = c match {
    case Error(_, e)                     => f(e, b)
    case Ok(_, a)                        => g(a, b)
    case w: Wrap[F, r, s, S, x, E, m, A] =>
      F.bifoldRight(w.inner, b)(
        (x, b1) => Eval.defer(bifoldRight1(w.stepFailure(x), b1)(f, g)),
        (m, b1) => Eval.defer(bifoldRight1(w.stepSuccess(m), b1)(f, g))
      )
  }

  def bifoldRight[A, B, C](fab: CalcM[F, Any, Any, S, A, B], c: Eval[C])(
      f: (A, Eval[C]) => Eval[C],
      g: (B, Eval[C]) => Eval[C]
  ): Eval[C] = bifoldRight1(fab.step((), ()), c)(f, g)
}

class CalcMBitraverse[F[+_, +_], S](implicit F: Bitraverse[F])
    extends CalcMBifoldable[F, S] with Bitraverse[CalcM[F, Any, Any, S, _, _]] {

  def bitraverse1[G[_]: Applicative, A, B, C, D](
      fab: StepResult[F, S, A, B]
  )(f: A => G[C], g: B => G[D]): G[CalcM[F, Any, Any, S, C, D]] = fab match {
    case Error(s, a)                     =>
      f(a).map(c => CalcM.set(s) >> CalcM.raise(c))
    case Ok(s, b)                        =>
      g(b).map(d => CalcM.set(s) >> CalcM.pure(d))
    case w: Wrap[F, r, s, S, x, A, m, B] =>
      F.bitraverse(w.inner)(
        x => bitraverse1(w.stepFailure(x))(f, g),
        m => bitraverse1(w.stepSuccess(m))(f, g)
      ).map(CalcM.roll)
  }

  def bitraverse[G[_]: Applicative, A, B, C, D](
      fab: CalcM[F, Any, Any, S, A, B]
  )(f: A => G[C], g: B => G[D]): G[CalcM[F, Any, Any, S, C, D]] = ???
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy