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

izumi.functional.bio.data.FreePanic.scala Maven / Gradle / Ivy

package izumi.functional.bio.data

import izumi.functional.bio.{Exit, Panic2}

import scala.annotation.nowarn
import scala.util.Try

sealed abstract class FreePanic[+S[_, _], +E, +A] {
  @inline final def flatMap[S1[e, a] >: S[e, a], B, E1 >: E](fun: A => FreePanic[S1, E1, B]): FreePanic[S1, E1, B] = FreePanic.FlatMapped[S1, E, E1, A, B](this, fun)
  @inline final def map[B](fun: A => B): FreePanic[S, E, B] = flatMap(a => FreePanic.pure[S, B](fun(a)))
  @inline final def as[B](as: => B): FreePanic[S, E, B] = map(_ => as)
  @inline final def *>[S1[e, a] >: S[e, a], B, E1 >: E](sc: FreePanic[S1, E1, B]): FreePanic[S1, E1, B] = flatMap(_ => sc)
  @inline final def <*[S1[e, a] >: S[e, a], B, E1 >: E](sc: FreePanic[S1, E1, B]): FreePanic[S1, E1, A] = flatMap(r => sc.as(r))

  @inline final def sandbox: FreePanic[S, Exit.FailureUninterrupted[E], A] =
    FreePanic.Sandbox(this)

  @inline final def redeem[S1[e, a] >: S[e, a], B, E1](err: E => FreePanic[S1, E1, B], succ: A => FreePanic[S1, E1, B]): FreePanic[S1, E1, B] =
    FreePanic.Redeem(this, err, succ)
  @inline final def redeemPure[B](err: E => B, succ: A => B): FreePanic[S, Nothing, B] =
    FreePanic.Redeem[S, E, Nothing, A, B](this, e => FreePanic.pure(err(e)), a => FreePanic.pure(succ(a)))
  @inline final def catchAll[S1[e, a] >: S[e, a], A1 >: A, E1](err: E => FreePanic[S1, E1, A1]): FreePanic[S1, E1, A1] =
    FreePanic.Redeem[S1, E, E1, A, A1](this, err, FreePanic.pure)
  @inline final def catchSome[S1[e, a] >: S[e, a], A2 >: A, E1 >: E](err: PartialFunction[E, FreePanic[S1, E1, A2]]): FreePanic[S1, E1, A2] =
    FreePanic.Redeem[S1, E, E1, A, A2](this, err.orElse { case e => FreePanic.fail(e) }, FreePanic.pure)
  @inline final def flip: FreePanic[S, A, E] =
    FreePanic.Redeem[S, E, A, A, E](this, FreePanic.pure, FreePanic.fail(_))

  @inline final def bracket[S1[e, a] >: S[e, a], B, E1 >: E](release: A => FreePanic[S1, Nothing, Unit])(use: A => FreePanic[S1, E1, B]): FreePanic[S1, E1, B] =
    FreePanic.BracketCase(this, (a: A, _: Exit[E1, B]) => release(a), use)
  @inline final def bracketCase[S1[e, a] >: S[e, a], B, E1 >: E](
    release: (A, Exit[E1, B]) => FreePanic[S1, Nothing, Unit]
  )(use: A => FreePanic[S1, E1, B]
  ): FreePanic[S1, E1, B] =
    FreePanic.BracketCase(this, release, use)
  @inline final def guarantee[S1[e, a] >: S[e, a]](g: FreePanic[S1, Nothing, Unit]): FreePanic[S1, E, A] =
    FreePanic.BracketCase[S1, E, A, A](this, (_: A, _: Exit[E, A]) => g, FreePanic.pure)

  @inline final def void: FreePanic[S, E, Unit] = map(_ => ())

  @inline final def mapK[S1[e, a] >: S[e, a], T[_, _]](f: S1 ~>> T): FreePanic[T, E, A] = {
    foldMap[S1, FreePanic[T, +_, +_]](Morphism2(FreePanic `lift` f(_)))
  }

  // FIXME: Scala 3.1.4 bug: false unexhaustive match warning
  @nowarn("msg=(pattern case: FreePanic.FlatMapped)|(Unreachable case)")
  @inline final def foldMap[S1[e, a] >: S[e, a], G[+_, +_]](transform: S1 ~>> G)(implicit G: Panic2[G]): G[E, A] = {
    this match {
      case FreePanic.Pure(a) => G.pure(a)
      case FreePanic.Suspend(a) => transform(a)
      case FreePanic.Fail(fail) => G.fail(fail())
      case FreePanic.Terminate(termination) => G.terminate(termination())
      case s: FreePanic.SelfInterrupt[S] @unchecked =>
        @inline def selfInterrupt[e, a](s: FreePanic[S, e, a] & FreePanic.SelfInterrupt[S]): G[e, a] = {
          (s: FreePanic[S, e, a] @unchecked) match {
            case FreePanic.SelfInterrupt() => G.sendInterruptToSelf
          }
        }
        selfInterrupt(s)
      case FreePanic.Redeem(sub, err, suc) =>
        sub
          .foldMap(transform).redeem(
            err(_).foldMap(transform),
            suc(_).foldMap(transform),
          )
      case s: FreePanic.Sandbox[S, E, A] @unchecked =>
        @inline def foldMapSandbox[e, a](s: FreePanic[S, e, a] & FreePanic.Sandbox[S, e, a]): G[e, a] = {
          (s: FreePanic[S, e, a] @unchecked) match {
            case FreePanic.Sandbox(sub) => sub.foldMap(transform).sandbox
          }
        }
        foldMapSandbox[E, A](s)
      case FreePanic.Uninterruptible(sub) =>
        G.uninterruptible(sub.foldMap(transform))
      case s: FreePanic.BracketCase[S, E, ?, A] @unchecked =>
        s.acquire.foldMap[S1, G](transform).bracketCase[E, A]((a, e: Exit[E, A]) => s.release(a, e).foldMap[S1, G](transform))(a => s.use(a).foldMap[S1, G](transform))
      case s: FreePanic.UninterruptibleExcept[S, E, A] @unchecked =>
        G.uninterruptibleExcept((r: RestoreInterruption2[G]) => s.sub(FreePanic.CapturedRestoreInterruption.captureRestore(r)).foldMap(transform))
      case s: FreePanic.BracketExcept[S, E, ?, A] @unchecked =>
        G.bracketExcept(r => s.acquire(FreePanic.CapturedRestoreInterruption.captureRestore(r)).foldMap(transform))(
          (a, e: Exit[E, A]) => s.release(a, e).foldMap(transform)
        )(a => s.use(a).foldMap(transform))
      case s: FreePanic.CapturedRestoreInterruption[S, G, E, A] @unchecked =>
        @inline def applyRestore[e, a](s: FreePanic[S, e, a] & FreePanic.CapturedRestoreInterruption[S, G, e, a]) = {
          s match {
            case FreePanic.CapturedRestoreInterruption(restoreInterruption2, sub) =>
              restoreInterruption2(sub.foldMap(transform))
          }
        }
        applyRestore(s)
      case FreePanic.FlatMapped(sub, cont) =>
        sub match {
          case FreePanic.FlatMapped(sub2, cont2) => sub2.flatMap(a => cont2(a).flatMap(cont)).foldMap(transform)
          case another => another.foldMap(transform).flatMap(cont(_).foldMap(transform))
        }
    }
  }
}

object FreePanic {
  @inline def unit[S[_, _]]: FreePanic[S, Nothing, Unit] = Pure(())
  @inline def pure[S[_, _], A](a: A): FreePanic[S, Nothing, A] = Pure(a)
  @inline def lift[S[_, _], E, A](s: S[E, A]): FreePanic[S, E, A] = Suspend(s)
  @inline def fail[S[_, _], E](e: => E): FreePanic[S, E, Nothing] = Fail(() => e)
  @inline def terminate[S[_, _]](e: => Throwable): FreePanic[S, Nothing, Nothing] = Terminate(() => e)
  @inline def sendInterruptToSelf[S[_, _]]: FreePanic[S, Nothing, Unit] = SelfInterrupt()

  @inline def uninterruptible[S[_, _], E, A](s: FreePanic[S, E, A]): FreePanic[S, E, A] = Uninterruptible(s)
  @inline def uninterruptibleExcept[S[_, _], E, A](s: RestoreInterruption2[FreePanic[S, +_, +_]] => FreePanic[S, E, A]): FreePanic[S, E, A] = UninterruptibleExcept(s)

  @inline def bracket[S[_, _], E, A0, A](
    acquire: FreePanic[S, E, A0]
  )(release: A0 => FreePanic[S, Nothing, Unit]
  )(use: A0 => FreePanic[S, E, A]
  ): FreePanic[S, E, A] = {
    BracketCase(acquire, (a: A0, _: Exit[E, A]) => release(a), use)
  }

  @inline def bracketCase[S[_, _], E, A0, A](
    acquire: FreePanic[S, E, A0]
  )(release: (A0, Exit[E, A]) => FreePanic[S, Nothing, Unit]
  )(use: A0 => FreePanic[S, E, A]
  ): FreePanic[S, E, A] = {
    BracketCase(acquire, release, use)
  }

  @inline def bracketExcept[S[_, _], E, A0, A](
    acquire: RestoreInterruption2[FreePanic[S, +_, +_]] => FreePanic[S, E, A0]
  )(release: A0 => FreePanic[S, Nothing, Unit]
  )(use: A0 => FreePanic[S, E, A]
  ): FreePanic[S, E, A] = {
    BracketExcept(acquire, (a: A0, _: Exit[E, A]) => release(a), use)
  }

  final case class Pure[S[_, _], A](a: A) extends FreePanic[S, Nothing, A] {
    override def toString: String = s"Pure:[$a]"
  }
  final case class Suspend[S[_, _], E, A](s: S[E, A]) extends FreePanic[S, E, A] {
    override def toString: String = s"Suspend:[$s]"
  }
  final case class Fail[S[_, _], E](fail: () => E) extends FreePanic[S, E, Nothing] {
    override def toString: String = s"Fail:[$fail]"
  }
  final case class Terminate[S[_, _]](termination: () => Throwable) extends FreePanic[S, Nothing, Nothing] {
    override def toString: String = s"Terminate:[$termination]"
  }
  final case class SelfInterrupt[S[_, _]]() extends FreePanic[S, Nothing, Unit] {
    override def toString: String = "SelfInterrupt"
  }
  final case class CapturedRestoreInterruption[S[_, _], G[+_, +_], E, A](restoreInterruption2: RestoreInterruption2[G], sub: FreePanic[S, E, A])
    extends FreePanic[S, E, A] {
    override def toString: String = s"CapturedRestoreInterruption:[capturedRestore=$restoreInterruption2,sub=$sub]"
  }
  object CapturedRestoreInterruption {
    // taken from doobie library
    def captureRestore[S[_, _], G[+_, +_]](restoreG: RestoreInterruption2[G]): RestoreInterruption2[FreePanic[S, +_, +_]] = {
      Morphism2(f => CapturedRestoreInterruption(restoreG, f))
    }
  }
  final case class FlatMapped[S[_, _], E, E1 >: E, A, B](sub: FreePanic[S, E, A], cont: A => FreePanic[S, E1, B]) extends FreePanic[S, E1, B] {
    override def toString: String = s"FlatMapped:[sub=$sub]"
  }
  final case class Sandbox[S[_, _], E, A](sub: FreePanic[S, E, A]) extends FreePanic[S, Exit.FailureUninterrupted[E], A] {
    override def toString: String = s"Sandbox:[sub=$sub]"
  }
  final case class Redeem[S[_, _], E, E1, A, B](sub: FreePanic[S, E, A], err: E => FreePanic[S, E1, B], suc: A => FreePanic[S, E1, B]) extends FreePanic[S, E1, B] {
    override def toString: String = s"Redeem:[sub=$sub]"
  }
  final case class Uninterruptible[S[_, _], E, A](sub: FreePanic[S, E, A]) extends FreePanic[S, E, A] {
    override def toString: String = s"Uninterruptible:[sub=$sub]"
  }
  final case class UninterruptibleExcept[S[_, _], E, A](sub: RestoreInterruption2[FreePanic[S, +_, +_]] => FreePanic[S, E, A]) extends FreePanic[S, E, A] {
    override def toString: String = s"UninterruptibleExcept:[sub=${sub.getClass.getSimpleName}]"
  }
  final case class BracketCase[S[_, _], E, A0, A](
    acquire: FreePanic[S, E, A0],
    release: (A0, Exit[E, A]) => FreePanic[S, Nothing, Unit],
    use: A0 => FreePanic[S, E, A],
  ) extends FreePanic[S, E, A] {
    override def toString: String = s"BracketCase:[acquire=$acquire;use=${use.getClass.getSimpleName};release=${release.getClass.getSimpleName}]"
  }
  final case class BracketExcept[S[_, _], E, A0, A](
    acquire: RestoreInterruption2[FreePanic[S, +_, +_]] => FreePanic[S, E, A0],
    release: (A0, Exit[E, A]) => FreePanic[S, Nothing, Unit],
    use: A0 => FreePanic[S, E, A],
  ) extends FreePanic[S, E, A] {
    override def toString: String = s"BracketExcept:[acquire=$acquire;use=${use.getClass.getSimpleName};release=${release.getClass.getSimpleName}]"
  }

  @inline implicit def FreePanicInstances[S[_, _]]: Panic2[FreePanic[S, +_, +_]] = Panic2Instance.asInstanceOf[Panic2Instance[S]]

  object Panic2Instance extends Panic2Instance[Nothing]
  class Panic2Instance[S[_, _]] extends Panic2[FreePanic[S, +_, +_]] {
    @inline override final def flatMap[E, A, B](r: FreePanic[S, E, A])(f: A => FreePanic[S, E, B]): FreePanic[S, E, B] = r.flatMap(f)
    @inline override final def *>[E, A, B](f: FreePanic[S, E, A], next: => FreePanic[S, E, B]): FreePanic[S, E, B] = f *> next
    @inline override final def <*[E, A, B](f: FreePanic[S, E, A], next: => FreePanic[S, E, B]): FreePanic[S, E, A] = f <* next
    @inline override final def as[E, A, B](r: FreePanic[S, E, A])(v: => B): FreePanic[S, E, B] = r.as(v)
    @inline override final def void[E, A](r: FreePanic[S, E, A]): FreePanic[S, E, Unit] = r.void
    @inline override final def sandbox[E, A](r: FreePanic[S, E, A]): FreePanic[S, Exit.FailureUninterrupted[E], A] = r.sandbox
    @inline override final def catchAll[E, A, E2](r: FreePanic[S, E, A])(f: E => FreePanic[S, E2, A]): FreePanic[S, E2, A] = r.catchAll(f)
    @inline override final def catchSome[E, A, E1 >: E](r: FreePanic[S, E, A])(f: PartialFunction[E, FreePanic[S, E1, A]]): FreePanic[S, E1, A] = r.catchSome(f)
    @inline override final def bracketCase[E, A, B](
      acquire: FreePanic[S, E, A]
    )(release: (A, Exit[E, B]) => FreePanic[S, Nothing, Unit]
    )(use: A => FreePanic[S, E, B]
    ): FreePanic[S, E, B] = acquire.bracketCase(release)(use)
    @inline override final def guarantee[E, A](f: FreePanic[S, E, A], cleanup: FreePanic[S, Nothing, Unit]): FreePanic[S, E, A] = f.guarantee(cleanup)
    @inline override final def uninterruptible[E, A](r: FreePanic[S, E, A]): FreePanic[S, E, A] = FreePanic.Uninterruptible(r)
    @inline override final def uninterruptibleExcept[E, A](r: RestoreInterruption2[FreePanic[S, +_, +_]] => FreePanic[S, E, A]): FreePanic[S, E, A] =
      FreePanic.UninterruptibleExcept(r)
    @inline override final def bracketExcept[E, A, B](
      acquire: RestoreInterruption2[FreePanic[S, +_, +_]] => FreePanic[S, E, A]
    )(release: (A, Exit[E, B]) => FreePanic[S, Nothing, Unit]
    )(use: A => FreePanic[S, E, B]
    ): FreePanic[S, E, B] = FreePanic.BracketExcept(acquire, release, use)

    @inline override final def pure[A](a: A): FreePanic[S, Nothing, A] = FreePanic.pure(a)

    @inline override final def fail[E](v: => E): FreePanic[S, E, Nothing] = FreePanic.fail(v)
    @inline override final def terminate(v: => Throwable): FreePanic[S, Nothing, Nothing] = FreePanic.terminate(v)
    @inline override final def sendInterruptToSelf: FreePanic[S, Nothing, Unit] = FreePanic.sendInterruptToSelf

    @inline override final def fromEither[E, V](effect: => Either[E, V]): FreePanic[S, E, V] = FreePanic.unit *> {
      effect match {
        case Left(value) => fail(value)
        case Right(value) => pure(value)
      }
    }

    @inline override final def fromOption[E, A](errorOnNone: => E)(effect: => Option[A]): FreePanic[S, E, A] = FreePanic.unit *> {
      effect match {
        case None => fail(errorOnNone)
        case Some(value) => pure(value)
      }
    }

    @inline override final def fromTry[A](effect: => Try[A]): FreePanic[S, Throwable, A] = fromEither(effect.toEither)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy