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

zio.ZIO.scala Maven / Gradle / Ivy

There is a newer version: 2.1.11
Show newest version
/*
 * Copyright 2017-2024 John A. De Goes and the ZIO Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package zio

import zio.internal.{FiberScope, Platform}
import zio.metrics.{MetricLabel, Metrics}
import zio.stacktracer.TracingImplicits.disableAutoTrace

import java.io.IOException
import java.util.function.IntFunction
import scala.annotation.implicitNotFound
import scala.collection.mutable.{Builder, ListBuffer}
import scala.concurrent.ExecutionContext
import scala.reflect.ClassTag
import scala.util.control.NoStackTrace
import izumi.reflect.macrortti.LightTypeTag

/**
 * A `ZIO[R, E, A]` value is an immutable value (called an "effect") that
 * describes an async, concurrent workflow. In order to be executed, the
 * workflow requires a value of type `ZEnvironment[R]`, and when executed, the
 * workflow will either produce a failure of type `E`, or a success of type `A`.
 *
 * ZIO effects may informally be thought of as functions of the following form:
 *
 * {{{
 * ZEnvironment[R] => Either[E, A]
 * }}}
 *
 * ZIO effects model resourceful interaction with the outside world, including
 * synchronous, asynchronous, concurrent, and parallel interaction.
 *
 * The async and concurrent operations of ZIO effects are powered by fibers,
 * which are lightweight, green threads that enable high scalability.
 *
 * To run an effect, you need a `Runtime`, which is capable of executing
 * effects. Runtimes bundle a thread pool together with the environment that
 * effects need.
 */
sealed trait ZIO[-R, +E, +A]
    extends Product
    with Serializable
    with ZIOPlatformSpecific[R, E, A]
    with ZIOVersionSpecific[R, E, A] {
  self =>

  /**
   * Returns a new effect that applies the specified aspect to this effect.
   * Aspects are "transformers" that modify the behavior of their input in some
   * well-defined way (for example, adding a timeout).
   */
  final def @@[LowerR <: UpperR, UpperR <: R, LowerE >: E, UpperE >: LowerE, LowerA >: A, UpperA >: LowerA](
    aspect: => ZIOAspect[LowerR, UpperR, LowerE, UpperE, LowerA, UpperA]
  )(implicit trace: Trace): ZIO[UpperR, LowerE, LowerA] =
    ZIO.suspendSucceed(aspect(self))

  /**
   * A symbolic alias for `orDie`.
   */
  final def !(implicit ev1: E <:< Throwable, ev2: CanFail[E], trace: Trace): ZIO[R, Nothing, A] =
    self.orDie

  /**
   * Returns an effect that executes both this effect and the specified effect,
   * in parallel, returning result of provided effect. If either side fails,
   * then the other side will be interrupted.
   */
  final def &>[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
    self.zipWithPar(that)((_, b) => b)

  /**
   * A variant of `flatMap` that ignores the value produced by this effect.
   */
  final def *>[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
    self.flatMap(_ => that)

  /**
   * Returns an effect that executes both this effect and the specified effect,
   * in parallel, this effect result returned. If either side fails, then the
   * other side will be interrupted.
   */
  final def <&[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, A] =
    self.zipWithPar(that)((a, _) => a)

  /**
   * Returns an effect that executes both this effect and the specified effect,
   * in parallel, combining their results into a tuple. If either side fails,
   * then the other side will be interrupted.
   */
  final def <&>[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit
    zippable: Zippable[A, B],
    trace: Trace
  ): ZIO[R1, E1, zippable.Out] =
    self.zipWithPar(that)((a, b) => zippable.zip(a, b))

  /**
   * Sequences the specified effect after this effect, but ignores the value
   * produced by the effect.
   */
  final def <*[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, A] =
    self.flatMap(a => that.as(a))

  /**
   * Sequentially zips this effect with the specified effect, combining the
   * results into a tuple.
   */
  final def <*>[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit
    zippable: Zippable[A, B],
    trace: Trace
  ): ZIO[R1, E1, zippable.Out] =
    self zip that

  /**
   * A symbolic alias for `orElseEither`.
   */
  final def <+>[R1 <: R, E1, B](
    that: => ZIO[R1, E1, B]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, Either[A, B]] =
    self.orElseEither(that)

  /**
   * Operator alias for `orElse`.
   */
  final def <>[R1 <: R, E2, A1 >: A](
    that: => ZIO[R1, E2, A1]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E2, A1] =
    orElse(that)

  /**
   * A symbolic alias for `raceEither`.
   */
  final def <|>[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit
    trace: Trace
  ): ZIO[R1, E1, Either[A, B]] =
    self.raceEither(that)

  /**
   * Returns the logical negation of the `Boolean` value returned by this
   * effect.
   */
  @deprecated("use negate", "2.0.6")
  final def unary_![R1 <: R, E1 >: E](implicit ev: A <:< Boolean, trace: Trace): ZIO[R1, E1, Boolean] =
    self.map(a => !ev(a))

  /**
   * Returns an effect that submerges the error case of an `Either` into the
   * `ZIO`. The inverse operation of `ZIO.either`.
   */
  final def absolve[E1 >: E, B](implicit ev: A IsSubtypeOfOutput Either[E1, B], trace: Trace): ZIO[R, E1, B] =
    self.flatMap(ev(_) match {
      case Right(v) => Exit.succeed(v)
      case Left(e)  => ZIO.fail(e)
    })

  /**
   * Attempts to convert defects into a failure, throwing away all information
   * about the cause of the failure.
   */
  final def absorb(implicit ev: E IsSubtypeOfError Throwable, trace: Trace): RIO[R, A] =
    absorbWith(ev)

  /**
   * Attempts to convert defects into a failure, throwing away all information
   * about the cause of the failure.
   */
  final def absorbWith(f: E => Throwable)(implicit trace: Trace): RIO[R, A] =
    self.sandbox
      .foldZIO(
        cause => Exit.failCause(Cause.fail(cause.squashWith(f))),
        ZIO.successFn
      )

  /**
   * Maps the success value of this effect to the specified constant value.
   */
  def as[B](b: => B)(implicit trace: Trace): ZIO[R, E, B] =
    self.map(_ => b)

  /**
   * Maps the success value of this effect to a left value.
   */
  final def asLeft(implicit trace: Trace): ZIO[R, E, Either[A, Nothing]] =
    map(Left(_))

  /**
   * Maps the error value of this effect to a left value.
   */
  final def asLeftError(implicit trace: Trace): ZIO[R, Either[E, Nothing], A] =
    mapError(Left(_))

  /**
   * Maps the success value of this effect to a right value.
   */
  final def asRight(implicit trace: Trace): ZIO[R, E, Either[Nothing, A]] =
    map(Right(_))

  /**
   * Maps the error value of this effect to a right value.
   */
  final def asRightError(implicit trace: Trace): ZIO[R, Either[Nothing, E], A] =
    mapError(Right(_))

  /**
   * Maps the success value of this effect to an optional value.
   */
  final def asSome(implicit trace: Trace): ZIO[R, E, Option[A]] =
    map(Some(_))

  /**
   * Maps the error value of this effect to an optional value.
   */
  final def asSomeError(implicit trace: Trace): ZIO[R, Option[E], A] =
    mapError(Some(_))

  /**
   * Returns a new effect that will not succeed with its value before first
   * waiting for the end of all child fibers forked by the effect.
   */
  final def awaitAllChildren(implicit trace: Trace): ZIO[R, E, A] =
    ensuringChildren(Fiber.awaitAll(_))

  /**
   * Returns an effect that, if evaluated, will return the cached result of this
   * effect. Cached results will expire after `timeToLive` duration.
   */
  final def cached(timeToLive: => Duration)(implicit trace: Trace): ZIO[R, Nothing, IO[E, A]] =
    cachedInvalidate(timeToLive).map(_._1)

  /**
   * Returns an effect that, if evaluated, will return the cached result of this
   * effect. Cached results will expire after `timeToLive` duration. In
   * addition, returns an effect that can be used to invalidate the current
   * cached value before the `timeToLive` duration expires.
   */
  final def cachedInvalidate(
    timeToLive0: => Duration
  )(implicit trace: Trace): ZIO[R, Nothing, (IO[E, A], UIO[Unit])] =
    ZIO.suspendSucceed {
      val timeToLive = timeToLive0

      def get(cache: Ref.Synchronized[Option[(Long, Promise[E, A])]]): ZIO[R, E, A] =
        ZIO.uninterruptibleMask { restore =>
          Clock.nanoTime.flatMap { time =>
            cache.modifyZIO {
              case Some((end, p)) if end - time > 0 =>
                Exit.succeed(p.await -> Some((end, p)))
              case _ =>
                Promise.make[E, A].map { p =>
                  val effect = self.onExit(p.done(_))
                  effect -> Some((time + timeToLive.toNanos, p))
                }
            }.flatMap(restore(_))
          }
        }

      def invalidate(cache: Ref.Synchronized[Option[(Long, Promise[E, A])]]): UIO[Unit] =
        cache.set(None)

      for {
        r     <- ZIO.environment[R]
        cache <- Ref.Synchronized.make[Option[(Long, Promise[E, A])]](None)
      } yield (get(cache).provideEnvironment(r), invalidate(cache))
    }

  /**
   * Recovers from all errors.
   *
   * {{{
   * openFile("config.json").catchAll(_ => ZIO.succeed(defaultConfig))
   * }}}
   */
  final def catchAll[R1 <: R, E2, A1 >: A](
    h: E => ZIO[R1, E2, A1]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E2, A1] =
    self.foldZIO[R1, E2, A1](h, ZIO.successFn)

  /**
   * A version of `catchAll` that gives you the (optional) trace of the error.
   */
  final def catchAllTrace[R1 <: R, E2, A1 >: A](
    h: ((E, StackTrace)) => ZIO[R1, E2, A1]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E2, A1] =
    self.foldTraceZIO[R1, E2, A1](h, ZIO.successFn)

  /**
   * Recovers from all errors with provided Cause.
   *
   * {{{
   * openFile("config.json").catchAllCause(_ => ZIO.succeed(defaultConfig))
   * }}}
   *
   * @see
   *   [[absorb]], [[sandbox]], [[mapErrorCause]] - other functions that can
   *   recover from defects
   */
  final def catchAllCause[R1 <: R, E2, A1 >: A](k: Cause[E] => ZIO[R1, E2, A1])(implicit
    trace: Trace
  ): ZIO[R1, E2, A1] =
    ZIO.FoldZIO(trace, self, ZIO.successFn[A], k)

  /**
   * Recovers from all defects with provided function.
   *
   * {{{
   * effect.catchSomeDefect(_ => backup())
   * }}}
   *
   * '''WARNING''': There is no sensible way to recover from defects. This
   * method should be used only at the boundary between ZIO and an external
   * system, to transmit information on a defect for diagnostic or explanatory
   * purposes.
   */
  final def catchAllDefect[R1 <: R, E1 >: E, A1 >: A](h: Throwable => ZIO[R1, E1, A1])(implicit
    trace: Trace
  ): ZIO[R1, E1, A1] =
    catchSomeDefect { case t => h(t) }

  /**
   * Recovers from all NonFatal Throwables.
   *
   * {{{
   * openFile("data.json").catchNonFatalOrDie(_ => openFile("backup.json"))
   * }}}
   */
  final def catchNonFatalOrDie[R1 <: R, E2, A1 >: A](
    h: E => ZIO[R1, E2, A1]
  )(implicit ev1: CanFail[E], ev2: E <:< Throwable, trace: Trace): ZIO[R1, E2, A1] = {

    def hh(e: E) =
      ZIO.isFatalWith(isFatal => if (isFatal(e)) ZIO.die(e) else h(e))
    self.foldZIO[R1, E2, A1](hh, ZIO.successFn)
  }

  /**
   * Recovers from some or all of the error cases.
   *
   * {{{
   * openFile("data.json").catchSome {
   *   case _: FileNotFoundException => openFile("backup.json")
   * }
   * }}}
   */
  final def catchSome[R1 <: R, E1 >: E, A1 >: A](
    pf: PartialFunction[E, ZIO[R1, E1, A1]]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, A1] = {
    def tryRescue(c: Cause[E]): ZIO[R1, E1, A1] =
      c.failureOrCause.fold(t => pf.applyOrElse(t, (_: E) => Exit.failCause(c)), Exit.failCause)

    self.foldCauseZIO[R1, E1, A1](tryRescue, ZIO.successFn)
  }

  /**
   * A version of `catchSome` that gives you the trace of the error.
   */
  final def catchSomeTrace[R1 <: R, E1 >: E, A1 >: A](
    pf: PartialFunction[(E, StackTrace), ZIO[R1, E1, A1]]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, A1] = {
    def tryRescue(c: Cause[E]): ZIO[R1, E1, A1] =
      c.failureTraceOrCause.fold(t => pf.applyOrElse(t, (_: (E, StackTrace)) => Exit.failCause(c)), Exit.failCause)

    self.foldCauseZIO[R1, E1, A1](tryRescue, ZIO.successFn)
  }

  /**
   * Recovers from some or all of the error cases with provided cause.
   *
   * {{{
   * openFile("data.json").catchSomeCause {
   *   case c if (c.interrupted) => openFile("backup.json")
   * }
   * }}}
   */
  final def catchSomeCause[R1 <: R, E1 >: E, A1 >: A](
    pf: PartialFunction[Cause[E], ZIO[R1, E1, A1]]
  )(implicit trace: Trace): ZIO[R1, E1, A1] = {
    def tryRescue(c: Cause[E]): ZIO[R1, E1, A1] =
      pf.applyOrElse(c, (_: Cause[E]) => Exit.failCause(c))

    self.foldCauseZIO[R1, E1, A1](tryRescue, ZIO.successFn)
  }

  /**
   * Recovers from some or all of the defects with provided partial function.
   *
   * {{{
   * effect.catchSomeDefect {
   *   case _: SecurityException => backup()
   * }
   * }}}
   *
   * '''WARNING''': There is no sensible way to recover from defects. This
   * method should be used only at the boundary between ZIO and an external
   * system, to transmit information on a defect for diagnostic or explanatory
   * purposes.
   */
  final def catchSomeDefect[R1 <: R, E1 >: E, A1 >: A](
    pf: PartialFunction[Throwable, ZIO[R1, E1, A1]]
  )(implicit trace: Trace): ZIO[R1, E1, A1] =
    unrefineWith(pf)(ZIO.failFn).catchAll(identity)

  /**
   * Returns an effect that succeeds with the cause of failure of this effect,
   * or `Cause.empty` if the effect did succeed.
   */
  final def cause(implicit trace: Trace): URIO[R, Cause[E]] =
    self.foldCause(c => c, _ => Cause.empty)

  /**
   * Fail with `e` if the supplied `PartialFunction` does not match, otherwise
   * succeed with the returned value.
   */
  final def collect[E1 >: E, B](e: => E1)(pf: PartialFunction[A, B])(implicit trace: Trace): ZIO[R, E1, B] =
    collectZIO(e)(pf.andThen(ZIO.successFn))

  /**
   * Fail with `e` if the supplied `PartialFunction` does not match, otherwise
   * continue with the returned value.
   */
  final def collectZIO[R1 <: R, E1 >: E, B](e: => E1)(pf: PartialFunction[A, ZIO[R1, E1, B]])(implicit
    trace: Trace
  ): ZIO[R1, E1, B] =
    self.flatMap(v => pf.applyOrElse[A, ZIO[R1, E1, B]](v, _ => ZIO.fail(e)))

  /**
   * Returns a new effect that will not supervise any fibers forked by this
   * effect.
   */
  final def daemonChildren(implicit trace: Trace): ZIO[R, E, A] =
    FiberRef.forkScopeOverride.locally(Some(FiberScope.global))(self)

  /**
   * Taps the effect, printing the result of calling `.toString` on the value.
   */
  final def debug(implicit trace: Trace): ZIO[R, E, A] =
    self
      .tap(value => ZIO.succeed(println(value)))
      .tapErrorCause(error => ZIO.succeed(println(s" $error")))

  /**
   * Taps the effect, printing the result of calling `.toString` on the value.
   * Prefixes the output with the given message.
   */
  final def debug(prefix: => String)(implicit trace: Trace): ZIO[R, E, A] =
    self
      .tap(value => ZIO.succeed(println(s"$prefix: $value")))
      .tapErrorCause(error => ZIO.succeed(println(s" $prefix: $error")))

  /**
   * Returns an effect that is delayed from this effect by the specified
   * [[zio.Duration]].
   */
  final def delay(duration: => Duration)(implicit trace: Trace): ZIO[R, E, A] =
    Clock.sleep(duration) *> self

  /**
   * Returns a new workflow that executes this one and captures the changes in
   * `FiberRef` values.
   */
  def diffFiberRefs(implicit trace: Trace): ZIO[R, E, (FiberRefs.Patch, A)] =
    summarized(ZIO.getFiberRefs)(FiberRefs.Patch.diff)

  /**
   * Returns an effect that is always interruptible, but whose interruption will
   * be performed in the background.
   *
   * This method is useful to create "fast interrupting" effects. For example,
   * if you call this on an acquire release effect, then even if the effect is
   * "stuck" in acquire or release, its interruption will return immediately,
   * while the acquire / release are performed in the background.
   *
   * See timeout and race for other applications.
   */
  final def disconnect(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.uninterruptibleMask(restore =>
      ZIO.fiberIdWith(fiberId =>
        for {
          fiber <- restore(self).forkDaemon
          a     <- restore(fiber.join).onInterrupt(fiber.interruptAsFork(fiberId))
        } yield a
      )
    )

  /**
   * Returns an effect whose failure and success have been lifted into an
   * `Either`. The resulting effect cannot fail, because the failure case has
   * been exposed as part of the `Either` success case.
   *
   * This method is useful for recovering from `ZIO` effects that may fail.
   *
   * The error parameter of the returned `ZIO` is `Nothing`, since it is
   * guaranteed the `ZIO` effect does not model failure.
   */
  final def either(implicit ev: CanFail[E], trace: Trace): URIO[R, Either[E, A]] =
    self.foldZIO(ZIO.succeedLeft, ZIO.succeedRight)

  /**
   * Returns an effect that, if this effect _starts_ execution, then the
   * specified `finalizer` is guaranteed to begin execution, whether this effect
   * succeeds, fails, or is interrupted.
   *
   * For use cases that need access to the effect's result, see [[ZIO#onExit]].
   *
   * Finalizers offer very powerful guarantees, but they are low-level, and
   * should generally not be used for releasing resources. For higher-level
   * logic built on `ensuring`, see `ZIO#acquireReleaseWith`.
   */
  final def ensuring[R1 <: R](finalizer: => URIO[R1, Any])(implicit trace: Trace): ZIO[R1, E, A] =
    onExit(_ => finalizer)

  /**
   * Acts on the children of this fiber (collected into a single fiber),
   * guaranteeing the specified callback will be invoked, whether or not this
   * effect succeeds.
   */
  final def ensuringChild[R1 <: R](f: Fiber[Any, Iterable[Any]] => ZIO[R1, Nothing, Any])(implicit
    trace: Trace
  ): ZIO[R1, E, A] =
    ensuringChildren(children => f(Fiber.collectAll(children)))

  /**
   * Acts on the children of this fiber, guaranteeing the specified callback
   * will be invoked, whether or not this effect succeeds.
   */
  def ensuringChildren[R1 <: R](
    children: Chunk[Fiber.Runtime[Any, Any]] => ZIO[R1, Nothing, Any]
  )(implicit trace: Trace): ZIO[R1, E, A] =
    Supervisor
      .track(true)
      .flatMap(supervisor => self.supervised(supervisor).ensuring(supervisor.value.flatMap(children)))

  /**
   * Returns an effect that ignores errors and runs repeatedly until it
   * eventually succeeds.
   */
  final def eventually(implicit ev: CanFail[E], trace: Trace): URIO[R, A] =
    self <> ZIO.yieldNow *> eventually

  /**
   * Returns an effect that semantically runs the effect on a fiber, producing
   * an [[zio.Exit]] for the completion value of the fiber.
   */
  final def exit(implicit trace: Trace): URIO[R, Exit[E, A]] =
    self.foldCause(Exit.failCause, Exit.succeed(_))

  /**
   * Extracts this effect as an [[zio.Exit]] and then applies the provided
   * function to produce a new effect
   */
  final def exitWith[R1 <: R, E1, B](f: Exit[E, A] => ZIO[R1, E1, B])(implicit
    trace: Trace
  ): ZIO[R1, E1, B] =
    self.foldCauseZIO(c => f(Exit.failCause(c)), a => f(Exit.succeed(a)))

  /**
   * Maps this effect to the default exit codes.
   */
  final def exitCode(implicit trace: Trace): URIO[R, ExitCode] =
    self.foldCause(
      _ => ExitCode.failure,
      _ => ExitCode.success
    )

  /**
   * Dies with specified `Throwable` if the predicate fails.
   */
  final def filterOrDie(p: A => Boolean)(t: => Throwable)(implicit trace: Trace): ZIO[R, E, A] =
    self.filterOrElse(p)(ZIO.die(t))

  /**
   * Dies with a [[java.lang.RuntimeException]] having the specified text
   * message if the predicate fails.
   */
  final def filterOrDieMessage(p: A => Boolean)(message: => String)(implicit trace: Trace): ZIO[R, E, A] =
    self.filterOrElse(p)(ZIO.dieMessage(message))

  /**
   * Dies with `t` if the predicate fails.
   */
  final def filterOrDieWith(p: A => Boolean)(t: A => Throwable)(implicit trace: Trace): ZIO[R, E, A] =
    self.filterOrElseWith(p)(a => ZIO.die(t(a)))

  /**
   * Supplies `zio` if the predicate fails.
   */
  final def filterOrElse[R1 <: R, E1 >: E, A1 >: A](p: A => Boolean)(zio: => ZIO[R1, E1, A1])(implicit
    trace: Trace
  ): ZIO[R1, E1, A1] =
    filterOrElseWith[R1, E1, A1](p)(_ => zio)

  /**
   * Applies `f` if the predicate fails.
   */
  final def filterOrElseWith[R1 <: R, E1 >: E, A1 >: A](
    p: A => Boolean
  )(f: A => ZIO[R1, E1, A1])(implicit trace: Trace): ZIO[R1, E1, A1] =
    self.flatMap {
      case v if !p(v) => f(v)
      case v          => Exit.succeed(v)
    }

  /**
   * Fails with `e` if the predicate fails.
   */
  final def filterOrFail[E1 >: E](p: A => Boolean)(e: => E1)(implicit trace: Trace): ZIO[R, E1, A] =
    filterOrElse[R, E1, A](p)(ZIO.fail(e))

  /**
   * Returns an effect that runs this effect and in case of failure, runs each
   * of the specified effects in order until one of them succeeds.
   */
  final def firstSuccessOf[R1 <: R, E1 >: E, A1 >: A](rest: => Iterable[ZIO[R1, E1, A1]])(implicit
    trace: Trace
  ): ZIO[R1, E1, A1] =
    ZIO.firstSuccessOf(self, rest)

  /**
   * Returns an effect that models the execution of this effect, followed by the
   * passing of its value to the specified continuation function `k`, followed
   * by the effect that it returns.
   *
   * {{{
   * val parsed = readFile("foo.txt").flatMap(file => parseFile(file))
   * }}}
   */
  def flatMap[R1 <: R, E1 >: E, B](k: A => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
    ZIO.FlatMap(trace, self, k)

  /**
   * Creates a composite effect that represents this effect followed by another
   * one that may depend on the error produced by this one.
   *
   * {{{
   * val parsed = readFile("foo.txt").flatMapError(error => logErrorToFile(error))
   * }}}
   */
  final def flatMapError[R1 <: R, E2](
    f: E => URIO[R1, E2]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E2, A] =
    flipWith(_ flatMap f)

  /**
   * Returns an effect that performs the outer effect first, followed by the
   * inner effect, yielding the value of the inner effect.
   *
   * This method can be used to "flatten" nested effects.
   */
  final def flatten[R1 <: R, E1 >: E, B](implicit
    ev1: A IsSubtypeOfOutput ZIO[R1, E1, B],
    trace: Trace
  ): ZIO[R1, E1, B] =
    self.flatMap(a => ev1(a))

  /**
   * Returns an effect that swaps the error/success cases. This allows you to
   * use all methods on the error channel, possibly before flipping back.
   */
  final def flip(implicit trace: Trace): ZIO[R, A, E] =
    self.foldZIO(ZIO.successFn, ZIO.failFn)

  /**
   * Swaps the error/value parameters, applies the function `f` and flips the
   * parameters back
   */
  final def flipWith[R1, A1, E1](f: ZIO[R, A, E] => ZIO[R1, A1, E1])(implicit trace: Trace): ZIO[R1, E1, A1] =
    f(self.flip).flip

  /**
   * Folds over the failure value or the success value to yield an effect that
   * does not fail, but succeeds with the value returned by the left or right
   * function passed to `fold`.
   */
  def fold[B](failure: E => B, success: A => B)(implicit ev: CanFail[E], trace: Trace): URIO[R, B] =
    foldZIO(e => ZIO.succeed(failure(e)), a => ZIO.succeed(success(a)))

  /**
   * A more powerful version of `fold` that allows recovering from any kind of
   * failure except external interruption.
   */
  def foldCause[B](failure: Cause[E] => B, success: A => B)(implicit trace: Trace): URIO[R, B] =
    foldCauseZIO(c => ZIO.succeed(failure(c)), a => ZIO.succeed(success(a)))

  /**
   * A more powerful version of `foldZIO` that allows recovering from any kind
   * of failure except external interruption.
   */
  def foldCauseZIO[R1 <: R, E2, B](
    failure: Cause[E] => ZIO[R1, E2, B],
    success: A => ZIO[R1, E2, B]
  )(implicit trace: Trace): ZIO[R1, E2, B] =
    ZIO.FoldZIO(trace, self, success, failure)

  /**
   * A version of `foldZIO` that gives you the trace of the error.
   */
  final def foldTraceZIO[R1 <: R, E2, B](
    failure: ((E, StackTrace)) => ZIO[R1, E2, B],
    success: A => ZIO[R1, E2, B]
  )(implicit
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R1, E2, B] =
    foldCauseZIO(c => c.failureTraceOrCause.fold(failure, Exit.failCause), success)

  /**
   * Recovers from errors by accepting one effect to execute for the case of an
   * error, and one effect to execute for the case of success.
   *
   * This method has better performance than `either` since no intermediate
   * value is allocated and does not require subsequent calls to `flatMap` to
   * define the next effect.
   *
   * The error parameter of the returned `IO` may be chosen arbitrarily, since
   * it will depend on the `IO`s returned by the given continuations.
   */
  final def foldZIO[R1 <: R, E2, B](failure: E => ZIO[R1, E2, B], success: A => ZIO[R1, E2, B])(implicit
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R1, E2, B] =
    foldCauseZIO(c => c.failureOrCause.fold(failure, Exit.failCause), success)

  /**
   * Returns a new effect that will pass the success value of this effect to the
   * provided callback. If this effect fails, then the failure will be ignored.
   */
  final def forEachZIO[R1 <: R, E2, B](f: A => ZIO[R1, E2, B])(implicit trace: Trace): ZIO[R1, E2, Option[B]] =
    self.foldCauseZIO(_ => ZIO.none, a => f(a).map(Some(_)))

  final def forever(implicit trace: Trace): ZIO[R, E, Nothing] = {
    lazy val loop: ZIO[R, E, Nothing] = self *> ZIO.yieldNow *> loop

    loop
  }

  /**
   * Returns an effect that forks this effect into its own separate fiber,
   * returning the fiber immediately, without waiting for it to begin executing
   * the effect.
   *
   * You can use the `fork` method whenever you want to execute an effect in a
   * new fiber, concurrently and without "blocking" the fiber executing other
   * effects. Using fibers can be tricky, so instead of using this method
   * directly, consider other higher-level methods, such as `raceWith`,
   * `zipPar`, and so forth.
   *
   * The fiber returned by this method has methods to interrupt the fiber and to
   * wait for it to finish executing the effect. See [[zio.Fiber]] for more
   * information.
   *
   * Whenever you use this method to launch a new fiber, the new fiber is
   * attached to the parent fiber's scope. This means when the parent fiber
   * terminates, the child fiber will be terminated as well, ensuring that no
   * fibers leak. This behavior is called "auto supervision", and if this
   * behavior is not desired, you may use the [[forkDaemon]] or [[forkIn]]
   * methods.
   *
   * {{{
   * for {
   *   fiber <- subtask.fork
   *   // Do stuff...
   *   a <- fiber.join
   * } yield a
   * }}}
   */
  final def fork(implicit trace: Trace): URIO[R, Fiber.Runtime[E, A]] =
    self.forkWithScopeOverride(null)

  /**
   * Forks the effect in the specified scope. The fiber will be interrupted when
   * the scope is closed.
   */
  final def forkIn(scope: => Scope)(implicit trace: Trace): URIO[R, Fiber.Runtime[E, A]] =
    ZIO.uninterruptibleMask { restore =>
      def interrupt(fiber: Fiber.Runtime[Any, Any]): ZIO[Any, Nothing, Any] =
        ZIO.fiberIdWith { fiberId =>
          if (fiberId == fiber.id) Exit.unit else fiber.interrupt
        }

      scope.fork.flatMap { child =>
        restore(self).onExit(child.close(_)).forkDaemon.tap { fiber =>
          child.addFinalizer(interrupt(fiber))
        }
      }
    }

  /**
   * Forks the effect into a new fiber attached to the global scope. Because the
   * new fiber is attached to the global scope, when the fiber executing the
   * returned effect terminates, the forked fiber will continue running.
   */
  final def forkDaemon(implicit trace: Trace): URIO[R, Fiber.Runtime[E, A]] =
    self.forkWithScopeOverride(FiberScope.global)

  /**
   * Forks the fiber in a [[Scope]], interrupting it when the scope is closed.
   */
  final def forkScoped(implicit trace: Trace): ZIO[R with Scope, Nothing, Fiber.Runtime[E, A]] =
    ZIO.scopeWith(scope => self.forkIn(scope))

  /**
   * Like fork but handles an error with the provided handler.
   */
  final def forkWithErrorHandler[R1 <: R](handler: E => URIO[R1, Any])(implicit
    trace: Trace
  ): URIO[R1, Fiber.Runtime[E, A]] =
    onError(c => c.failureOrCause.fold(handler, Exit.failCause)).fork

  private[zio] final def forkWithScopeOverride(
    scopeOverride: FiberScope
  )(implicit trace: Trace): URIO[R, Fiber.Runtime[E, A]] =
    ZIO.withFiberRuntime[R, Nothing, Fiber.Runtime[E, A]] { (parentFiber, parentStatus) =>
      val f = ZIO.succeed(
        ZIO.unsafe.fork(trace, self, parentFiber, parentStatus.runtimeFlags, scopeOverride)(Unsafe)
      )
      if (parentFiber.shouldYieldBeforeFork()) ZIO.yieldNow *> f else f
    }

  /**
   * Unwraps the optional error, defaulting to the provided value.
   */
  final def flattenErrorOption[E1, E2 <: E1](default: => E2)(implicit
    ev: E IsSubtypeOfError Option[E1],
    trace: Trace
  ): ZIO[R, E1, A] =
    self.mapError(e => ev(e).getOrElse(default))

  /**
   * Returns a successful effect with the head of the list if the list is
   * non-empty or fails with the error `None` if the list is empty.
   */
  final def head[B](implicit ev: A IsSubtypeOfOutput List[B], trace: Trace): ZIO[R, Option[E], B] =
    self.foldZIO(
      e => Exit.fail(Some(e)),
      a => ev(a).headOption.fold[ZIO[R, Option[E], B]](Exit.failNone)(ZIO.successFn)
    )

  /**
   * Returns a new effect that ignores the success or failure of this effect.
   */
  final def ignore(implicit trace: Trace): URIO[R, Unit] =
    self.fold(ZIO.unitFn, ZIO.unitFn)

  /**
   * Returns a new effect that ignores the success or failure of this effect,
   * but which also logs failures at the Debug level, just in case the failure
   * turns out to be important.
   */
  final def ignoreLogged(implicit trace: Trace): URIO[R, Unit] =
    self.foldCauseZIO(
      cause =>
        ZIO.logLevel(LogLevel.Debug) {
          ZIO.logCause("An error was silently ignored because it is not anticipated to be useful", cause)
        },
      _ => Exit.unit
    )

  /**
   * Returns a new effect that will not succeed with its value before first
   * interrupting all child fibers forked by the effect.
   */
  final def interruptAllChildren(implicit trace: Trace): ZIO[R, E, A] =
    ensuringChildren(Fiber.interruptAll(_))

  /**
   * Returns a new effect that performs the same operations as this effect, but
   * interruptibly, even if composed inside of an uninterruptible region.
   *
   * Note that effects are interruptible by default, so this function only has
   * meaning if used within an uninterruptible region.
   *
   * WARNING: This operator "punches holes" into effects, allowing them to be
   * interrupted in unexpected places. Do not use this operator unless you know
   * exactly what you are doing. Instead, you should use
   * [[ZIO.uninterruptibleMask]].
   */
  final def interruptible(implicit trace: Trace): ZIO[R, E, A] =
    withRuntimeFlags(RuntimeFlags.enableInterruption)

  /**
   * Switches the interrupt status for this effect. If `true` is used, then the
   * effect becomes interruptible (the default), while if `false` is used, then
   * the effect becomes uninterruptible. These changes are compositional, so
   * they only affect regions of the effect.
   */
  final def interruptStatus(flag: => InterruptStatus)(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed {
      if (flag.isInterruptible) self.interruptible
      else self.uninterruptible
    }

  /**
   * Returns an effect that keeps or breaks a promise based on the result of
   * this effect. Synchronizes interruption, so if this effect is interrupted,
   * the specified promise will be interrupted, too.
   */
  final def intoPromise[E1 >: E, A1 >: A](p: => Promise[E1, A1])(implicit trace: Trace): URIO[R, Boolean] =
    ZIO.uninterruptibleMask(restore => restore(self).exitWith(p.done(_)))

  /**
   * Returns whether this effect is a failure.
   */
  final def isFailure(implicit trace: Trace): URIO[R, Boolean] =
    foldZIO(_ => Exit.`true`, _ => Exit.`false`)

  /**
   * Returns whether this effect is a success.
   */
  final def isSuccess(implicit trace: Trace): URIO[R, Boolean] =
    foldZIO(_ => Exit.`false`, _ => Exit.`true`)

  /**
   * "Zooms in" on the value in the `Left` side of an `Either`, moving the
   * possibility that the value is a `Right` to the error channel.
   */
  final def left[B, C](implicit ev: A IsSubtypeOfOutput Either[B, C], trace: Trace): ZIO[R, Either[E, C], B] =
    self.foldZIO(
      e => Exit.fail(Left(e)),
      a => ev(a).fold(ZIO.successFn, c => ZIO.fail(Right(c)))
    )

  /**
   * Performs the specified operation while "zoomed in" on the `Left` case of an
   * `Either`.
   */
  final def leftWith[R1, E1, A1, B, B1, C, C1](
    f: ZIO[R, Either[E, C], B] => ZIO[R1, Either[E1, C1], B1]
  )(implicit ev: A IsSubtypeOfOutput Either[B, C], trace: Trace): ZIO[R1, E1, Either[B1, C1]] =
    f(self.left).unleft

  /**
   * Logs the cause of failure of this workflow.
   */
  final def logError(implicit trace: Trace): ZIO[R, E, A] =
    logError("")

  /**
   * Logs the cause of failure of this workflow with the specified message.
   */
  final def logError(message: => String)(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.uninterruptibleMask { restore =>
      restore(self).tapErrorCause { cause =>
        ZIO.logErrorCause(message, cause)
      }
    }

  /**
   * Adjusts the label for the current logging span.
   * {{{
   * parseRequest(req).logSpan("parsing")
   * }}}
   */
  def logSpan(label: => String)(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.logSpan(label)(self)

  /**
   * Returns an effect whose success is mapped by the specified `f` function.
   */
  def map[B](f: A => B)(implicit trace: Trace): ZIO[R, E, B] =
    flatMap(a => ZIO.succeed(f(a)))

  /**
   * Returns an effect whose success is mapped by the specified side effecting
   * `f` function, translating any thrown exceptions into typed failed effects.
   */
  final def mapAttempt[B](f: A => B)(implicit ev: E IsSubtypeOfError Throwable, trace: Trace): RIO[R, B] =
    foldZIO(e => Exit.fail(ev(e)), a => ZIO.attempt(f(a)))

  /**
   * Returns an effect whose failure and success channels have been mapped by
   * the specified pair of functions, `f` and `g`.
   */
  def mapBoth[E2, B](f: E => E2, g: A => B)(implicit ev: CanFail[E], trace: Trace): ZIO[R, E2, B] =
    foldZIO(e => Exit.fail(f(e)), a => ZIO.succeed(g(a)))

  /**
   * Returns an effect with its error channel mapped using the specified
   * function. This can be used to lift a "smaller" error into a "larger" error.
   */
  def mapError[E2](f: E => E2)(implicit ev: CanFail[E], trace: Trace): ZIO[R, E2, A] =
    self.mapErrorCause(_.map(f))

  /**
   * Returns an effect with its full cause of failure mapped using the specified
   * function. This can be used to transform errors while preserving the
   * original structure of `Cause`.
   *
   * @see
   *   [[absorb]], [[sandbox]], [[catchAllCause]] - other functions for dealing
   *   with defects
   */
  def mapErrorCause[E2](h: Cause[E] => Cause[E2])(implicit trace: Trace): ZIO[R, E2, A] =
    self.foldCauseZIO(c => Exit.failCause(h(c)), ZIO.successFn)

  /**
   * Returns an effect that, if evaluated, will return the lazily computed
   * result of this effect.
   */
  final def memoize(implicit trace: Trace): UIO[ZIO[R, E, A]] =
    for {
      promise  <- Promise.make[E, (FiberRefs.Patch, A)]
      complete <- self.diffFiberRefs.intoPromise(promise).once
    } yield complete *> promise.await.flatMap { case (patch, a) => ZIO.patchFiberRefs(patch).as(a) }

  /**
   * Returns a new effect where the error channel has been merged into the
   * success channel to their common combined type.
   */
  final def merge[A1 >: A](implicit ev1: E IsSubtypeOfError A1, ev2: CanFail[E], trace: Trace): URIO[R, A1] =
    self.foldZIO(e => Exit.succeed(ev1(e)), ZIO.successFn)

  /**
   * Returns a new effect where boolean value of this effect is negated.
   */
  final def negate(implicit ev: A IsSubtypeOfOutput Boolean, trace: Trace): ZIO[R, E, Boolean] =
    map(result => !ev(result))

  /**
   * Requires the option produced by this value to be `None`.
   */
  final def none[B](implicit ev: A IsSubtypeOfOutput Option[B], trace: Trace): ZIO[R, Option[E], Unit] =
    self.foldZIO(
      e => Exit.fail(Some(e)),
      a => ev(a).fold[ZIO[R, Option[E], Unit]](Exit.unit)(_ => Exit.failNone)
    )

  /**
   * Returns an effect that will be executed at most once, even if it is
   * evaluated multiple times.
   */
  final def once(implicit trace: Trace): UIO[ZIO[R, E, Unit]] =
    Ref.make(true).map(ref => self.whenZIO(ref.getAndSet(false)).unit)

  final def onDone[R1 <: R](
    error: E => ZIO[R1, Nothing, Any],
    success: A => ZIO[R1, Nothing, Any]
  )(implicit trace: Trace): ZIO[R1, Nothing, Unit] =
    ZIO.uninterruptibleMask { restore =>
      restore(self).foldZIO(e => restore(error(e)), s => restore(success(s))).forkDaemon.unit
    }

  final def onDoneCause[R1 <: R](
    error: Cause[E] => ZIO[R1, Nothing, Any],
    success: A => ZIO[R1, Nothing, Any]
  )(implicit trace: Trace): ZIO[R1, Nothing, Unit] =
    ZIO.uninterruptibleMask { restore =>
      restore(self).foldCauseZIO(e => restore(error(e)), s => restore(success(s))).forkDaemon.unit
    }

  /**
   * Runs the specified effect if this effect fails, providing the error to the
   * effect if it exists. The provided effect will not be interrupted.
   */
  final def onError[R1 <: R](cleanup: Cause[E] => URIO[R1, Any])(implicit trace: Trace): ZIO[R1, E, A] =
    onExit {
      case _: Exit.Success[?]  => Exit.unit
      case Exit.Failure(cause) => cleanup(cause)
    }

  /**
   * Returns an effect which is guaranteed to be executed on the specified
   * executor. The specified effect will always run on the specified executor,
   * even in the presence of asynchronous boundaries.
   *
   * This is useful when an effect must be executed somewhere, for example: on a
   * UI thread, inside a client library's thread pool, inside a blocking thread
   * pool, inside a low-latency thread pool, or elsewhere.
   *
   * The `onExecutor` function composes with the innermost `onExecutor` taking
   * priority. Use of this method does not alter the execution semantics of
   * other effects composed with this one, making it easy to compositionally
   * reason about where effects are running.
   */
  final def onExecutor(executor: => Executor)(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.onExecutor(executor)(self)

  /**
   * Executes the effect on the specified `ExecutionContext` and then shifts
   * back to the default one.
   */
  final def onExecutionContext(ec: => ExecutionContext)(implicit trace: Trace): ZIO[R, E, A] =
    self.onExecutor(Executor.fromExecutionContext(ec))

  /**
   * Ensures that a cleanup functions runs, whether this effect succeeds, fails,
   * or is interrupted.
   */
  final def onExit[R1 <: R](cleanup: Exit[E, A] => URIO[R1, Any])(implicit trace: Trace): ZIO[R1, E, A] =
    ZIO.uninterruptibleMask { restore =>
      restore(self).foldCauseZIO(
        failure1 => {
          val result = Exit.failCause(failure1)
          cleanup(result).foldCauseZIO(
            failure2 => Exit.failCause(failure1 ++ failure2),
            _ => result
          )
        },
        success => {
          val result = Exit.succeed(success)

          cleanup(result).flatMap(_ => result)
        }
      )
    }

  /**
   * Runs the specified effect if this effect is interrupted.
   */
  final def onInterrupt[R1 <: R](cleanup: => URIO[R1, Any])(implicit trace: Trace): ZIO[R1, E, A] =
    onExit {
      case _: Exit.Success[?]  => Exit.unit
      case Exit.Failure(cause) => if (cause.isInterruptedOnly) cleanup else Exit.unit
    }

  /**
   * Calls the specified function, and runs the effect it returns, if this
   * effect is interrupted.
   */
  final def onInterrupt[R1 <: R](cleanup: Set[FiberId] => URIO[R1, Any])(implicit trace: Trace): ZIO[R1, E, A] =
    // TODO: isInterrupted or isInterruptedOnly?
    onExit {
      case _: Exit.Success[?]  => Exit.unit
      case Exit.Failure(cause) => if (cause.isInterruptedOnly) cleanup(cause.interruptors) else Exit.unit
    }

  /**
   * Runs the specified effect if this effect is terminated, either because of a
   * defect or because of interruption.
   */
  final def onTermination[R1 <: R](
    cleanup: Cause[Nothing] => URIO[R1, Any]
  )(implicit trace: Trace): ZIO[R1, E, A] =
    onExit {
      case _: Exit.Success[?] => Exit.unit
      case Exit.Failure(cause) =>
        if (cause.isFailure) Exit.unit
        else cleanup(cause.asInstanceOf[Cause[Nothing]])
    }

  /**
   * Executes this effect, skipping the error but returning optionally the
   * success.
   */
  final def option(implicit ev: CanFail[E], trace: Trace): URIO[R, Option[A]] =
    self.foldZIO(_ => Exit.none, a => Exit.succeed(Some(a)))

  /**
   * Translates effect failure into death of the fiber, making all failures
   * unchecked and not a part of the type of the effect.
   */
  final def orDie(implicit ev1: E IsSubtypeOfError Throwable, ev2: CanFail[E], trace: Trace): URIO[R, A] =
    orDieWith(ev1)

  /**
   * Keeps none of the errors, and terminates the fiber with them, using the
   * specified function to convert the `E` into a `Throwable`.
   */
  final def orDieWith(f: E => Throwable)(implicit ev: CanFail[E], trace: Trace): URIO[R, A] =
    mapErrorCause(_.flatMap(e => Cause.die(f(e))))

  /**
   * Unearth the unchecked failure of the effect. (opposite of `orDie`)
   * {{{
   *   val f0: Task[Unit] = ZIO.fail(new Exception("failing")).unit
   *   val f1: UIO[Unit]  = f0.orDie
   *   val f2: Task[Unit] = f1.resurrect
   * }}}
   */
  final def resurrect(implicit ev1: E IsSubtypeOfError Throwable, trace: Trace): RIO[R, A] =
    self.unrefineWith { case e => e }(ev1)

  /**
   * Executes this effect and returns its value, if it succeeds, but otherwise
   * executes the specified effect.
   */
  final def orElse[R1 <: R, E2, A1 >: A](
    that: => ZIO[R1, E2, A1]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E2, A1] =
    tryOrElse(that, ZIO.successFn)

  /**
   * Returns an effect that will produce the value of this effect, unless it
   * fails, in which case, it will produce the value of the specified effect.
   */
  final def orElseEither[R1 <: R, E2, B](
    that: => ZIO[R1, E2, B]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E2, Either[A, B]] =
    tryOrElse(that.map(Right(_)), ZIO.succeedLeft)

  /**
   * Executes this effect and returns its value, if it succeeds, but otherwise
   * fails with the specified error.
   */
  def orElseFail[E1](e1: => E1)(implicit ev: CanFail[E], trace: Trace): ZIO[R, E1, A] =
    orElse(ZIO.fail(e1))

  /**
   * Returns an effect that will produce the value of this effect, unless it
   * fails with the `None` value, in which case it will produce the value of the
   * specified effect.
   */
  final def orElseOptional[R1 <: R, E1, A1 >: A](
    that: => ZIO[R1, Option[E1], A1]
  )(implicit ev: E IsSubtypeOfError Option[E1], trace: Trace): ZIO[R1, Option[E1], A1] =
    catchAll(ev(_).fold(that)(e => Exit.fail(Some(e))))

  /**
   * Executes this effect and returns its value, if it succeeds, but otherwise
   * succeeds with the specified value.
   */
  final def orElseSucceed[A1 >: A](a1: => A1)(implicit ev: CanFail[E], trace: Trace): URIO[R, A1] =
    orElse(ZIO.succeed(a1))

  /**
   * Exposes all parallel errors in a single call
   */
  final def parallelErrors[E1 >: E](implicit trace: Trace): ZIO[R, ::[E1], A] =
    self.foldCauseZIO(
      cause =>
        cause.failures match {
          case Nil            => Exit.failCause(cause.asInstanceOf[Cause[Nothing]])
          case ::(head, tail) => Exit.failCause(Cause.fail(::[E1](head, tail), cause.trace))
        },
      ZIO.successFn
    )

  /**
   * Returns a new scoped workflow that runs finalizers added to the scope of
   * this workflow in parallel.
   */
  final def parallelFinalizers(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.parallelFinalizers(self)

  /**
   * Provides the `ZIO` effect with its required environment, which eliminates
   * its dependency on `R`.
   */
  final def provideEnvironment(r: => ZEnvironment[R])(implicit trace: Trace): IO[E, A] =
    FiberRef.currentEnvironment.locally(r)(self.asInstanceOf[ZIO[Any, E, A]])

  /**
   * Provides a layer to the ZIO effect, which translates it to another level.
   */
  final def provideLayer[E1 >: E, R0](
    layer: => ZLayer[R0, E1, R]
  )(implicit trace: Trace): ZIO[R0, E1, A] =
    ZIO.scopedWith(scope => layer.build(scope).flatMap(r => self.provideEnvironment(r)))

  /**
   * Transforms the environment being provided to this effect with the specified
   * function.
   */
  final def provideSomeEnvironment[R0](
    f: ZEnvironment[R0] => ZEnvironment[R]
  )(implicit trace: Trace): ZIO[R0, E, A] =
    ZIO.environmentWithZIO(r0 => self.provideEnvironment(f(r0)))

  /**
   * Splits the environment into two parts, providing one part using the
   * specified layer and leaving the remainder `R0`.
   *
   * {{{
   * val zio: ZIO[Logging with Database, Nothing, Unit] = ???
   *
   * val loggingLayer: ZLayer[Any, Nothing, Logging] = ???
   *
   * val zio2 = zio.provideSomeLayer[Database](loggingLayer)
   * }}}
   */
  final def provideSomeLayer[R0]: ZIO.ProvideSomeLayer[R0, R, E, A] =
    new ZIO.ProvideSomeLayer[R0, R, E, A](self)

  /**
   * Returns an effect that races this effect with the specified effect,
   * returning the first successful `A` from the faster side. If one effect
   * succeeds, the other will be interrupted. If neither succeeds, then the
   * effect will fail with some error.
   *
   * WARNING: The raced effect will safely interrupt the "loser", but will not
   * resume until the loser has been cleanly terminated. If early return is
   * desired, then instead of performing `l race r`, perform `l.disconnect race
   * r.disconnect`, which disconnects left and right interrupt signal, allowing
   * a fast return, with interruption performed in the background.
   */
  final def race[R1 <: R, E1 >: E, A1 >: A](that: => ZIO[R1, E1, A1])(implicit trace: Trace): ZIO[R1, E1, A1] =
    ZIO.fiberIdWith { parentFiberId =>
      (self.raceWith(that))(
        (exit, right) =>
          exit.foldExitZIO[Any, E1, A1](
            cause => right.join.mapErrorCause(cause && _),
            a => right.interruptAs(parentFiberId).as(a)
          ),
        (exit, left) =>
          exit.foldExitZIO[Any, E1, A1](
            cause => left.join.mapErrorCause(_ && cause),
            a => left.interruptAs(parentFiberId).as(a)
          )
      )
    }

  @deprecated("use race", "2.0.7")
  final def raceAwait[R1 <: R, E1 >: E, A1 >: A](that: => ZIO[R1, E1, A1])(implicit trace: Trace): ZIO[R1, E1, A1] =
    self race that

  /**
   * Returns an effect that races this effect with all the specified effects,
   * yielding the value of the first effect to succeed with a value. Losers of
   * the race will be interrupted immediately
   */
  final def raceAll[R1 <: R, E1 >: E, A1 >: A](
    ios0: => Iterable[ZIO[R1, E1, A1]]
  )(implicit trace: Trace): ZIO[R1, E1, A1] = ZIO.suspendSucceed {
    val ios = ios0

    def arbiter[E1, A1](
      fibers: List[Fiber[E1, A1]],
      winner: Fiber[E1, A1],
      promise: Promise[E1, (A1, Fiber[E1, A1])],
      fails: Ref[Int]
    )(res: Exit[E1, A1]): URIO[R1, Any] =
      res.foldExitZIO[R1, Nothing, Unit](
        e => ZIO.flatten(fails.modify((c: Int) => (if (c == 0) promise.failCause(e).unit else Exit.unit) -> (c - 1))),
        a =>
          promise
            .succeed(a -> winner)
            .flatMap(set =>
              if (set) fibers.foldLeft(ZIO.unit)((io, f) => if (f eq winner) io else io <* f.interrupt)
              else ZIO.unit
            )
      )

    (for {
      done  <- Promise.make[E1, (A1, Fiber[E1, A1])]
      fails <- Ref.make[Int](ios.size)
      c <- ZIO.uninterruptibleMask { restore =>
             for {
               fs <- ZIO.foreach(self :: ios.toList)(io => ZIO.interruptible(io).fork)
               _ <- fs.foldLeft[ZIO[R1, E1, Any]](ZIO.unit) { case (io, f) =>
                      io *> f.await.flatMap(arbiter(fs, f, done, fails)).fork
                    }

               inheritAll = { (res: (A1, Fiber[E1, A1])) => res._2.inheritAll.as(res._1) }

               c <- restore(done.await.flatMap(inheritAll))
                      .onInterrupt(fs.foldLeft(ZIO.unit)((io, f) => io <* f.interrupt))
             } yield c
           }
    } yield c)
  }

  /**
   * Returns an effect that races this effect with the specified effect,
   * yielding the first result to complete, whether by success or failure. If
   * neither effect completes, then the composed effect will not complete.
   *
   * WARNING: The raced effect will safely interrupt the "loser", but will not
   * resume until the loser has been cleanly terminated. If early return is
   * desired, then instead of performing `l raceFirst r`, perform `l.disconnect
   * raceFirst r.disconnect`, which disconnects left and right interrupt signal,
   * allowing a fast return, with interruption performed in the background.
   */
  final def raceFirst[R1 <: R, E1 >: E, A1 >: A](that: => ZIO[R1, E1, A1])(implicit
    trace: Trace
  ): ZIO[R1, E1, A1] =
    (self.exit race that.exit).unexit

  @deprecated("use raceFirst", "2.0.7")
  final def raceFirstAwait[R1 <: R, E1 >: E, A1 >: A](that: => ZIO[R1, E1, A1])(implicit
    trace: Trace
  ): ZIO[R1, E1, A1] =
    self raceFirst that

  /**
   * Returns an effect that races this effect with the specified effect,
   * yielding the first result to succeed. If neither effect succeeds, then the
   * composed effect will fail with some error.
   *
   * WARNING: The raced effect will safely interrupt the "loser", but will not
   * resume until the loser has been cleanly terminated. If early return is
   * desired, then instead of performing `l raceEither r`, perform `l.disconnect
   * raceEither r.disconnect`, which disconnects left and right interrupt
   * signal, allowing the earliest possible return.
   */
  final def raceEither[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit
    trace: Trace
  ): ZIO[R1, E1, Either[A, B]] =
    self.map(Left(_)) race that.map(Right(_))

  /**
   * Forks this effect and the specified effect into their own fibers, and races
   * them, calling one of two specified callbacks depending on which fiber wins
   * the race. This method does not interrupt, join, or otherwise do anything
   * with the fibers. It can be considered a low-level building block for
   * higher-level operators like `race`.
   */
  private final def raceFibersWith[R1 <: R, ER, E2, B, C](right: ZIO[R1, ER, B])(
    leftWins: (Fiber.Runtime[E, A], Fiber.Runtime[ER, B]) => ZIO[R1, E2, C],
    rightWins: (Fiber.Runtime[ER, B], Fiber.Runtime[E, A]) => ZIO[R1, E2, C],
    leftScope: FiberScope = null,
    rightScope: FiberScope = null
  )(implicit trace: Trace): ZIO[R1, E2, C] =
    ZIO.withFiberRuntime[R1, E2, C] { (parentFiber, parentStatus) =>
      import java.util.concurrent.atomic.AtomicBoolean

      implicit val unsafe: Unsafe = Unsafe

      val parentRuntimeFlags = parentStatus.runtimeFlags

      @inline def complete[E0, E1, A, B](
        winner: Fiber.Runtime[E0, A],
        loser: Fiber.Runtime[E1, B],
        cont: (Fiber.Runtime[E0, A], Fiber.Runtime[E1, B]) => ZIO[R1, E2, C],
        ab: AtomicBoolean,
        cb: ZIO[R1, E2, C] => Any
      ): Any =
        if (ab.compareAndSet(true, false)) {
          cb(cont(winner, loser))
        }

      val raceIndicator = new AtomicBoolean(true)

      val leftFiber  = ZIO.unsafe.makeChildFiber(trace, self, parentFiber, parentRuntimeFlags, leftScope)
      val rightFiber = ZIO.unsafe.makeChildFiber(trace, right, parentFiber, parentRuntimeFlags, rightScope)

      val startLeftFiber  = leftFiber.startSuspended()
      val startRightFiber = rightFiber.startSuspended()

      ZIO
        .async[R1, E2, C](
          { cb =>
            leftFiber.addObserver { _ =>
              complete(leftFiber, rightFiber, leftWins, raceIndicator, cb)
            }

            rightFiber.addObserver { _ =>
              complete(rightFiber, leftFiber, rightWins, raceIndicator, cb)
            }

            startLeftFiber(self)
            startRightFiber(right)
          },
          leftFiber.id <> rightFiber.id
        )
    }

  /**
   * Returns an effect that races this effect with the specified effect, calling
   * the specified finisher as soon as one result or the other has been
   * computed.
   */
  final def raceWith[R1 <: R, E1, E2, B, C](that: => ZIO[R1, E1, B])(
    leftDone: (Exit[E, A], Fiber[E1, B]) => ZIO[R1, E2, C],
    rightDone: (Exit[E1, B], Fiber[E, A]) => ZIO[R1, E2, C]
  )(implicit trace: Trace): ZIO[R1, E2, C] =
    self.raceFibersWith(that)(
      (winner, loser) =>
        winner.await.flatMap {
          case exit: Exit.Success[?] =>
            winner.inheritAll.flatMap(_ => leftDone(exit, loser))
          case exit: Exit.Failure[_] =>
            leftDone(exit, loser)
        },
      (winner, loser) =>
        winner.await.flatMap {
          case exit: Exit.Success[B] =>
            winner.inheritAll.flatMap(_ => rightDone(exit, loser))
          case exit: Exit.Failure[E1] =>
            rightDone(exit, loser)
        }
    )

  /**
   * Keeps some of the errors, and terminates the fiber with the rest
   */
  final def refineOrDie[E1](
    pf: PartialFunction[E, E1]
  )(implicit ev1: E IsSubtypeOfError Throwable, ev2: CanFail[E], trace: Trace): ZIO[R, E1, A] =
    refineOrDieWith(pf)(ev1)

  /**
   * Keeps some of the errors, and terminates the fiber with the rest, using the
   * specified function to convert the `E` into a `Throwable`.
   */
  final def refineOrDieWith[E1](pf: PartialFunction[E, E1])(
    f: E => Throwable
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R, E1, A] =
    mapErrorCause(_.flatMap(pf.andThen(Cause.fail(_)).applyOrElse(_, (e: E) => Cause.die(f(e)))))

  /**
   * Fail with the returned value if the `PartialFunction` matches, otherwise
   * continue with our held value.
   */
  final def reject[E1 >: E](pf: PartialFunction[A, E1])(implicit trace: Trace): ZIO[R, E1, A] =
    rejectZIO(pf.andThen(ZIO.fail(_)))

  /**
   * Continue with the returned computation if the `PartialFunction` matches,
   * translating the successful match into a failure, otherwise continue with
   * our held value.
   */
  final def rejectZIO[R1 <: R, E1 >: E](
    pf: PartialFunction[A, ZIO[R1, E1, E1]]
  )(implicit trace: Trace): ZIO[R1, E1, A] =
    self.flatMap { v =>
      pf.andThen[ZIO[R1, E1, A]](_.flatMap(ZIO.failFn))
        .applyOrElse[A, ZIO[R1, E1, A]](v, ZIO.successFn)
    }

  /**
   * Returns a new effect that repeats this effect according to the specified
   * schedule or until the first failure. Scheduled recurrences are in addition
   * to the first execution, so that `io.repeat(Schedule.once)` yields an effect
   * that executes `io`, and then if that succeeds, executes `io` an additional
   * time.
   */
  final def repeat[R1 <: R, B](schedule: => Schedule[R1, A, B])(implicit
    trace: Trace
  ): ZIO[R1, E, B] =
    repeatOrElse[R1, E, B](schedule, (e, _) => Exit.fail(e))

  /**
   * Returns a new effect that repeats this effect the specified number of times
   * or until the first failure. Repeats are in addition to the first execution,
   * so that `io.repeatN(1)` yields an effect that executes `io`, and then if
   * that succeeds, executes `io` an additional time.
   */
  final def repeatN(n: => Int)(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed {
      def loop(n: Int): ZIO[R, E, A] =
        self.flatMap(a => if (n <= 0) Exit.succeed(a) else ZIO.yieldNow *> loop(n - 1))

      loop(n)
    }

  /**
   * Returns a new effect that repeats this effect according to the specified
   * schedule or until the first failure, at which point, the failure value and
   * schedule output are passed to the specified handler.
   *
   * Scheduled recurrences are in addition to the first execution, so that
   * `io.repeat(Schedule.once)` yields an effect that executes `io`, and then if
   * that succeeds, executes `io` an additional time.
   */
  final def repeatOrElse[R1 <: R, E2, B](
    schedule: => Schedule[R1, A, B],
    orElse: (E, Option[B]) => ZIO[R1, E2, B]
  )(implicit trace: Trace): ZIO[R1, E2, B] =
    repeatOrElseEither[R1, B, E2, B](schedule, orElse).map(_.merge)

  /**
   * Returns a new effect that repeats this effect according to the specified
   * schedule or until the first failure, at which point, the failure value and
   * schedule output are passed to the specified handler.
   *
   * Scheduled recurrences are in addition to the first execution, so that
   * `io.repeat(Schedule.once)` yields an effect that executes `io`, and then if
   * that succeeds, executes `io` an additional time.
   */
  final def repeatOrElseEither[R1 <: R, B, E2, C](
    schedule0: => Schedule[R1, A, B],
    orElse: (E, Option[B]) => ZIO[R1, E2, C]
  )(implicit trace: Trace): ZIO[R1, E2, Either[C, B]] =
    ZIO.suspendSucceed {
      val schedule = schedule0

      schedule.driver.flatMap { driver =>
        def loop(a: A): ZIO[R1, E2, Either[C, B]] =
          driver
            .next(a)
            .foldZIO(
              _ => driver.last.orDie.map(Right(_)),
              b =>
                self.foldZIO(
                  e => orElse(e, Some(b)).map(Left(_)),
                  a => loop(a)
                )
            )

        self.foldZIO(
          e => orElse(e, None).map(Left(_)),
          a => loop(a)
        )
      }
    }

  /**
   * Repeats this effect until its value satisfies the specified predicate or
   * until the first failure.
   */
  final def repeatUntil(p: A => Boolean)(implicit trace: Trace): ZIO[R, E, A] =
    repeatUntilZIO(a => ZIO.succeed(p(a)))

  /**
   * Repeats this effect until its value is equal to the specified value or
   * until the first failure.
   */
  final def repeatUntilEquals[A1 >: A](a: => A1)(implicit trace: Trace): ZIO[R, E, A1] =
    repeatUntil(_ == a)

  /**
   * Repeats this effect until its value satisfies the specified effectful
   * predicate or until the first failure.
   */
  final def repeatUntilZIO[R1 <: R](f: A => URIO[R1, Boolean])(implicit trace: Trace): ZIO[R1, E, A] =
    self.flatMap(a => f(a).flatMap(b => if (b) Exit.succeed(a) else ZIO.yieldNow *> repeatUntilZIO(f)))

  /**
   * Repeats this effect while its value satisfies the specified predicate or
   * until the first failure.
   */
  final def repeatWhile(p: A => Boolean)(implicit trace: Trace): ZIO[R, E, A] =
    repeatWhileZIO(a => ZIO.succeed(p(a)))

  /**
   * Repeats this effect for as long as its value is equal to the specified
   * value or until the first failure.
   */
  final def repeatWhileEquals[A1 >: A](a: => A1)(implicit trace: Trace): ZIO[R, E, A1] =
    repeatWhile(_ == a)

  /**
   * Repeats this effect while its value satisfies the specified effectful
   * predicate or until the first failure.
   */
  final def repeatWhileZIO[R1 <: R](f: A => URIO[R1, Boolean])(implicit trace: Trace): ZIO[R1, E, A] =
    repeatUntilZIO(e => f(e).map(!_))

  /**
   * Performs this effect the specified number of times and collects the
   * results.
   */
  final def replicateZIO(n: => Int)(implicit trace: Trace): ZIO[R, E, Iterable[A]] =
    ZIO.replicateZIO(n)(self)

  /**
   * Performs this effect the specified number of times, discarding the results.
   */
  final def replicateZIODiscard(n: => Int)(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.replicateZIODiscard(n)(self)

  /**
   * Retries with the specified retry policy. Retries are done following the
   * failure of the original `io` (up to a fixed maximum with `once` or `recurs`
   * for example), so that that `io.retry(Schedule.once)` means "execute `io`
   * and in case of failure, try again once".
   */
  final def retry[R1 <: R, S](
    policy: => Schedule[R1, E, S]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E, A] =
    ZIO.suspendSucceed {

      def loop(driver: Schedule.Driver[Any, R1, E, S]): ZIO[R1, E, A] =
        self.catchAllCause { cause =>
          cause.failureOrCause.fold(
            e =>
              driver
                .next(e)
                .foldZIO(
                  _ => driver.last.orDie.flatMap(_ => Exit.failCause(cause)),
                  _ => loop(driver)
                ),
            cause => Exit.failCause(cause)
          )
        }

      policy.driver.flatMap(loop(_))
    }

  /**
   * Retries this effect the specified number of times.
   */
  final def retryN(n: => Int)(implicit ev: CanFail[E], trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed {

      def loop(n: Int): ZIO[R, E, A] =
        self.catchAllCause { cause =>
          cause.failureOrCause.fold(
            _ => if (n <= 0) Exit.failCause(cause) else ZIO.yieldNow *> loop(n - 1),
            cause => Exit.failCause(cause)
          )
        }

      loop(n)
    }

  /**
   * Retries with the specified schedule, until it fails, and then both the
   * value produced by the schedule together with the last error are passed to
   * the recovery function.
   */
  final def retryOrElse[R1 <: R, A1 >: A, S, E1](
    policy: => Schedule[R1, E, S],
    orElse: (E, S) => ZIO[R1, E1, A1]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, A1] =
    retryOrElseEither(policy, orElse).map(_.merge)

  /**
   * Returns an effect that retries this effect with the specified schedule when
   * it fails, until the schedule is done, then both the value produced by the
   * schedule together with the last error are passed to the specified recovery
   * function.
   */
  final def retryOrElseEither[R1 <: R, Out, E1, B](
    schedule0: => Schedule[R1, E, Out],
    orElse: (E, Out) => ZIO[R1, E1, B]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, Either[B, A]] =
    ZIO.suspendSucceed {
      val schedule = schedule0

      def loop(driver: Schedule.Driver[Any, R1, E, Out]): ZIO[R1, E1, Either[B, A]] =
        self
          .map(Right(_))
          .catchAll(e =>
            driver
              .next(e)
              .foldZIO(
                _ => driver.last.orDie.flatMap(out => orElse(e, out).map(Left(_))),
                _ => loop(driver)
              )
          )

      schedule.driver.flatMap(loop(_))
    }

  /**
   * Retries this effect until its error satisfies the specified predicate.
   */
  final def retryUntil(f: E => Boolean)(implicit ev: CanFail[E], trace: Trace): ZIO[R, E, A] =
    retryUntilZIO(e => ZIO.succeed(f(e)))

  /**
   * Retries this effect until its error is equal to the specified error.
   */
  final def retryUntilEquals[E1 >: E](e: => E1)(implicit ev: CanFail[E1], trace: Trace): ZIO[R, E1, A] =
    retryUntil(_ == e)

  /**
   * Retries this effect until its error satisfies the specified effectful
   * predicate.
   */
  final def retryUntilZIO[R1 <: R](
    f: E => URIO[R1, Boolean]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E, A] =
    self.catchAll(e => f(e).flatMap(b => if (b) Exit.fail(e) else ZIO.yieldNow *> retryUntilZIO(f)))

  /**
   * Retries this effect while its error satisfies the specified predicate.
   */
  final def retryWhile(f: E => Boolean)(implicit ev: CanFail[E], trace: Trace): ZIO[R, E, A] =
    retryWhileZIO(e => ZIO.succeed(f(e)))

  /**
   * Retries this effect for as long as its error is equal to the specified
   * error.
   */
  final def retryWhileEquals[E1 >: E](e: => E1)(implicit ev: CanFail[E1], trace: Trace): ZIO[R, E1, A] =
    retryWhile(_ == e)

  /**
   * Retries this effect while its error satisfies the specified effectful
   * predicate.
   */
  final def retryWhileZIO[R1 <: R](
    f: E => URIO[R1, Boolean]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E, A] =
    retryUntilZIO(e => f(e).map(!_))

  /**
   * "Zooms in" on the value in the `Right` side of an `Either`, moving the
   * possibility that the value is a `Left` to the error channel.
   */
  final def right[B, C](implicit ev: A IsSubtypeOfOutput Either[B, C], trace: Trace): ZIO[R, Either[B, E], C] =
    self.foldZIO(
      e => Exit.fail(Right(e)),
      a => ev(a).fold(b => ZIO.fail(Left(b)), ZIO.successFn)
    )

  /**
   * Performs the specified operation while "zoomed in" on the `Right` case of
   * an `Either`.
   */
  final def rightWith[R1, E1, A1, B, B1, C, C1](
    f: ZIO[R, Either[B, E], C] => ZIO[R1, Either[B1, E1], C1]
  )(implicit ev: A IsSubtypeOfOutput Either[B, C], trace: Trace): ZIO[R1, E1, Either[B1, C1]] =
    f(self.right).unright

  /**
   * Exposes the full cause of failure of this effect.
   *
   * {{{
   * final case class DomainError()
   *
   * val veryBadIO: IO[DomainError, Unit] =
   *   ZIO.succeed(5 / 0) *> ZIO.fail(DomainError())
   *
   * val caught: IO[DomainError, Unit] =
   *   veryBadIO.sandbox.mapError(_.untraced).catchAll {
   *     case Cause.Die(_: ArithmeticException) =>
   *       // Caught defect: divided by zero!
   *       ZIO.unit
   *     case Cause.Fail(_) =>
   *       // Caught error: DomainError!
   *       ZIO.unit
   *     case cause =>
   *       // Caught unknown defects, shouldn't recover!
   *       ZIO.refailCause(cause)
   *   }
   * }}}
   */
  final def sandbox(implicit trace: Trace): ZIO[R, Cause[E], A] =
    foldCauseZIO(ZIO.failFn, ZIO.successFn)

  /**
   * Runs this effect according to the specified schedule.
   *
   * See [[scheduleFrom]] for a variant that allows the schedule's decision to
   * depend on the result of this effect.
   */
  final def schedule[R1 <: R, B](schedule: => Schedule[R1, Any, B])(implicit
    trace: Trace
  ): ZIO[R1, E, B] =
    scheduleFrom(())(schedule)

  /**
   * Runs this effect according to the specified schedule starting from the
   * specified input value.
   */
  final def scheduleFrom[R1 <: R, A1 >: A, B](a: => A1)(
    schedule0: => Schedule[R1, A1, B]
  )(implicit trace: Trace): ZIO[R1, E, B] =
    ZIO.suspendSucceed {
      val schedule = schedule0

      schedule.driver.flatMap { driver =>
        def loop(a: A1): ZIO[R1, E, B] =
          driver.next(a).foldZIO(_ => driver.last.orDie, _ => self.flatMap(loop))

        loop(a)
      }
    }

  /**
   * Runs this effect according to the specified schedule in a new fiber
   * attached to the current scope.
   */
  final def scheduleFork[R1 <: R, B](schedule: => Schedule[R1, Any, B])(implicit
    trace: Trace
  ): ZIO[R1 with Scope, Nothing, Fiber.Runtime[E, B]] =
    self.schedule(schedule).forkScoped

  /**
   * Returns a new scoped workflow that runs finalizers added to the scope of
   * this workflow sequentially in the reverse of the order in which they were
   * added. Note that finalizers are run sequentially by default so this only
   * has meaning if used within a scope where finalizers are being run in
   * parallel.
   */
  final def sequentialFinalizers(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.sequentialFinalizers(self)

  /**
   * Converts an option on values into an option on errors.
   */
  final def some[B](implicit ev: A IsSubtypeOfOutput Option[B], trace: Trace): ZIO[R, Option[E], B] =
    self.foldZIO(
      e => Exit.fail(Some(e)),
      a => ev(a).fold[ZIO[R, Option[E], B]](ZIO.fail(Option.empty[E]))(ZIO.successFn)
    )

  /**
   * Perfoms the specified operation while "zoomed in" on the `Some` case of an
   * `Option`.
   */
  final def someWith[R1, E1, A1, B, B1](
    f: ZIO[R, Option[E], B] => ZIO[R1, Option[E1], B1]
  )(implicit ev: A IsSubtypeOfOutput Option[B], trace: Trace): ZIO[R1, E1, Option[B1]] =
    f(self.some).unsome

  /**
   * Extracts the optional value, or returns the given 'default'. Superseded by
   * `someOrElse` with better type inference. This method was left for binary
   * compatibility.
   */
  protected final def someOrElse[B](
    default: => B
  )(implicit ev: A IsSubtypeOfOutput Option[B], trace: Trace): ZIO[R, E, B] =
    map(a => ev(a).getOrElse(default))

  /**
   * Extracts the optional value, or returns the given 'default'.
   */
  final def someOrElse[B, C](
    default: => C
  )(implicit ev0: A IsSubtypeOfOutput Option[B], ev1: C <:< B, trace: Trace): ZIO[R, E, B] =
    map(a => ev0(a).getOrElse(default))

  /**
   * Extracts the optional value, or executes the effect 'default'. Superseded
   * by someOrElseZIO with better type inference. This method was left for
   * binary compatibility.
   */
  protected final def someOrElseZIO[B, R1 <: R, E1 >: E](
    default: => ZIO[R1, E1, B]
  )(implicit ev: A IsSubtypeOfOutput Option[B], trace: Trace): ZIO[R1, E1, B] =
    self.flatMap(ev(_) match {
      case Some(value) => Exit.succeed(value)
      case None        => default
    })

  /**
   * Extracts the optional value, or executes the effect 'default'.
   */
  final def someOrElseZIO[B, R1 <: R, E1 >: E, C](
    default: => ZIO[R1, E1, C]
  )(implicit ev0: A IsSubtypeOfOutput Option[B], ev1: C <:< B, trace: Trace): ZIO[R1, E1, B] =
    self.flatMap(ev0(_) match {
      case Some(value) => Exit.succeed(value)
      case None        => default.map(ev1)
    })

  /**
   * Extracts the optional value, or fails with the given error 'e'.
   */
  final def someOrFail[B, E1 >: E](
    e: => E1
  )(implicit ev: A IsSubtypeOfOutput Option[B], trace: Trace): ZIO[R, E1, B] =
    self.flatMap(ev(_) match {
      case Some(value) => Exit.succeed(value)
      case None        => ZIO.fail(e)
    })

  /**
   * Extracts the optional value, or fails with a
   * [[java.util.NoSuchElementException]]
   */
  final def someOrFailException[B, E1 >: E](implicit
    ev: A IsSubtypeOfOutput Option[B],
    ev2: NoSuchElementException <:< E1,
    trace: Trace
  ): ZIO[R, E1, B] =
    self.foldCauseZIO(
      e => Exit.failCause(e),
      ev(_) match {
        case Some(value) => Exit.succeed(value)
        case None        => ZIO.fail(ev2(new NoSuchElementException("None.get")))
      }
    )

  /**
   * Companion helper to `sandbox`. Allows recovery, and partial recovery, from
   * errors and defects alike, as in:
   *
   * {{{
   * case class DomainError()
   *
   * val veryBadIO: IO[DomainError, Unit] =
   *   ZIO.succeed(5 / 0) *> ZIO.fail(DomainError())
   *
   * val caught: IO[DomainError, Unit] =
   *   veryBadIO.sandboxWith[Any, DomainError, Unit](_.catchSome {
   *     case Cause.Die(_: ArithmeticException, _)=>
   *       // Caught defect: divided by zero!
   *       ZIO.succeed(0)
   *   })
   * }}}
   *
   * Using `sandboxWith` with `catchSome` is better than using
   * `io.sandbox.catchAll` with a partial match, because in the latter, if the
   * match fails, the original defects will be lost and replaced by a
   * `MatchError`
   */
  final def sandboxWith[R1 <: R, E2, B](f: ZIO[R1, Cause[E], A] => ZIO[R1, Cause[E2], B])(implicit
    trace: Trace
  ): ZIO[R1, E2, B] =
    ZIO.unsandbox(f(self.sandbox))

  /**
   * Summarizes a effect by computing some value before and after execution, and
   * then combining the values to produce a summary, together with the result of
   * execution.
   */
  final def summarized[R1 <: R, E1 >: E, B, C](
    summary0: => ZIO[R1, E1, B]
  )(f: (B, B) => C)(implicit trace: Trace): ZIO[R1, E1, (C, A)] =
    ZIO.suspendSucceed {
      val summary = summary0

      for {
        start <- summary
        value <- self
        end   <- summary
      } yield (f(start, end), value)
    }

  /**
   * Returns an effect with the behavior of this one, but where all child fibers
   * forked in the effect are reported to the specified supervisor.
   */
  final def supervised(supervisor: => Supervisor[Any])(implicit trace: Trace): ZIO[R, E, A] =
    FiberRef.currentSupervisor.locallyWith(_ ++ supervisor)(self)

  /**
   * Returns an effect that effectfully "peeks" at the success of this effect.
   *
   * {{{
   * readFile("data.json").tap(printLine)
   * }}}
   */
  final def tap[R1 <: R, E1 >: E](f: A => ZIO[R1, E1, Any])(implicit trace: Trace): ZIO[R1, E1, A] =
    self.flatMap(a => f(a).as(a))

  /**
   * Returns an effect that effectfully "peeks" at the failure or success of
   * this effect.
   * {{{
   * readFile("data.json").tapBoth(logError(_), logData(_))
   * }}}
   */
  final def tapBoth[R1 <: R, E1 >: E](f: E => ZIO[R1, E1, Any], g: A => ZIO[R1, E1, Any])(implicit
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R1, E1, A] =
    self.foldCauseZIO(c => c.failureOrCause.fold(f(_) *> Exit.failCause(c), _ => Exit.failCause(c)), a => g(a).as(a))

  /**
   * Returns an effect that effectually "peeks" at the defect of this effect.
   */
  final def tapDefect[R1 <: R, E1 >: E](f: Cause[Nothing] => ZIO[R1, E1, Any])(implicit
    trace: Trace
  ): ZIO[R1, E1, A] =
    self.catchAllCause { cause =>
      cause.keepDefects.fold[ZIO[R1, E1, A]](Exit.failCause(cause))(f(_) *> Exit.failCause(cause))
    }

  /**
   * Returns an effect that effectfully "peeks" at the result of this effect.
   * {{{
   * readFile("data.json").tapEither(result => log(result.fold("Error: " + _, "Success: " + _)))
   * }}}
   */
  final def tapEither[R1 <: R, E1 >: E](f: Either[E, A] => ZIO[R1, E1, Any])(implicit
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R1, E1, A] =
    self.foldCauseZIO(
      c => c.failureOrCause.fold(e => f(Left(e)) *> Exit.failCause(c), _ => Exit.failCause(c)),
      a => f(Right(a)).as(a)
    )

  /**
   * Returns an effect that effectfully "peeks" at the failure of this effect.
   * {{{
   * readFile("data.json").tapError(logError(_))
   * }}}
   */
  final def tapError[R1 <: R, E1 >: E](
    f: E => ZIO[R1, E1, Any]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, A] =
    self.foldCauseZIO(c => c.failureOrCause.fold(f(_) *> Exit.failCause(c), _ => Exit.failCause(c)), ZIO.successFn)

  /**
   * Returns an effect that effectually "peeks" at the cause of the failure of
   * this effect.
   * {{{
   * readFile("data.json").tapErrorCause(logCause(_))
   * }}}
   */
  final def tapErrorCause[R1 <: R, E1 >: E](f: Cause[E] => ZIO[R1, E1, Any])(implicit
    trace: Trace
  ): ZIO[R1, E1, A] =
    self.foldCauseZIO(c => f(c) *> Exit.failCause(c), ZIO.successFn)

  /**
   * A version of `tapError` that gives you the trace of the error.
   */
  final def tapErrorTrace[R1 <: R, E1 >: E](
    f: ((E, StackTrace)) => ZIO[R1, E1, Any]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, A] =
    self.foldCauseZIO(
      c => c.failureTraceOrCause.fold(f(_) *> Exit.failCause(c), _ => Exit.failCause(c)),
      ZIO.successFn
    )

  /**
   * Returns an effect that effectfully "peeks" at the success of this effect.
   * If the partial function isn't defined at the input, the result is
   * equivalent to the original effect.
   *
   * {{{
   * readFile("data.json").tapSome {
   *   case content if content.nonEmpty => putStrLn(content)
   * }
   * }}}
   */
  final def tapSome[R1 <: R, E1 >: E](f: PartialFunction[A, ZIO[R1, E1, Any]])(implicit
    trace: Trace
  ): ZIO[R1, E1, A] =
    self.tap(f.applyOrElse(_, (_: A) => Exit.unit))

  /**
   * Returns an effect that effectfully "peeks" at the failure of this effect.
   * If the partial function isn't defined at the input, the result is
   * equivalent to the original effect.
   */
  final def tapSomeError[R1 <: R, E1 >: E](
    f: PartialFunction[E, ZIO[R1, E1, Any]]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R1, E1, A] =
    self.tapError(f.applyOrElse(_, (_: E) => Exit.unit))

  /**
   * Returns a new effect that executes this one and times the execution.
   */
  final def timed(implicit trace: Trace): ZIO[R, E, (Duration, A)] =
    timedWith(Clock.nanoTime)

  /**
   * A more powerful variation of `timed` that allows specifying the workflow
   * that will be used to calculate the current time.
   */
  final def timedWith[R1 <: R, E1 >: E](nanoTime: => ZIO[R1, E1, Long])(implicit
    trace: Trace
  ): ZIO[R1, E1, (Duration, A)] =
    summarized(nanoTime)((start, end) => Duration.fromNanos(end - start))

  /**
   * Returns an effect that will timeout this effect, returning `None` if the
   * timeout elapses before the effect has produced a value; and returning
   * `Some` of the produced value otherwise.
   *
   * If the timeout elapses without producing a value, the running effect will
   * be safely interrupted.
   *
   * WARNING: The effect returned by this method will not itself return until
   * the underlying effect is actually interrupted. This leads to more
   * predictable resource utilization. If early return is desired, then instead
   * of using `effect.timeout(d)`, use `effect.disconnect.timeout(d)`, which
   * first disconnects the effect's interruption signal before performing the
   * timeout, resulting in earliest possible return, before an underlying effect
   * has been successfully interrupted.
   */
  final def timeout(d: => Duration)(implicit trace: Trace): ZIO[R, E, Option[A]] =
    timeoutTo(None)(Some(_))(d)

  /**
   * The same as [[timeout]], but instead of producing a `None` in the event of
   * timeout, it will produce the specified error.
   */
  final def timeoutFail[E1 >: E](e: => E1)(d: => Duration)(implicit
    trace: Trace
  ): ZIO[R, E1, A] =
    ZIO.flatten(timeoutTo(ZIO.fail(e))(ZIO.successFn)(d))

  /**
   * The same as [[timeout]], but instead of producing a `None` in the event of
   * timeout, it will produce the specified failure.
   */
  final def timeoutFailCause[E1 >: E](cause: => Cause[E1])(d: => Duration)(implicit
    trace: Trace
  ): ZIO[R, E1, A] =
    ZIO.flatten(timeoutTo(Exit.failCause(cause))(ZIO.successFn)(d))

  /**
   * Returns an effect that will timeout this effect, returning either the
   * default value if the timeout elapses before the effect has produced a
   * value; and or returning the result of applying the function `f` to the
   * success value of the effect.
   *
   * If the timeout elapses without producing a value, the running effect will
   * be safely interrupted
   *
   * {{{
   * ZIO.succeed(1).timeoutTo(None)(Some(_))(1.second)
   * }}}
   */
  final def timeoutTo[B](b: => B): ZIO.TimeoutTo[R, E, A, B] =
    new ZIO.TimeoutTo(self, () => b)

  /**
   * Converts the effect into a [[scala.concurrent.Future]].
   */
  final def toFuture(implicit ev2: E IsSubtypeOfError Throwable, trace: Trace): URIO[R, CancelableFuture[A]] =
    self toFutureWith ev2

  /**
   * Converts the effect into a [[scala.concurrent.Future]].
   */
  final def toFutureWith(f: E => Throwable)(implicit trace: Trace): URIO[R, CancelableFuture[A]] =
    self.fork.flatMap(_.toFutureWith(f))

  /**
   * When this effect succeeds with a cause, then this method returns a new
   * effect that either fails with the cause that this effect succeeded with, or
   * succeeds with unit, depending on whether the cause is empty.
   *
   * This operation is the opposite of [[cause]].
   */
  final def uncause[E1 >: E](implicit ev: A IsSubtypeOfOutput Cause[E1], trace: Trace): ZIO[R, E1, Unit] =
    self.flatMap { a =>
      val cause = ev(a)

      if (cause.isEmpty) Exit.unit
      else Exit.failCause(cause)
    }

  /**
   * Performs this effect uninterruptibly. This will prevent the effect from
   * being terminated externally, but the effect may fail for internal reasons
   * (e.g. an uncaught error) or terminate due to defect.
   *
   * Uninterruptible effects may recover from all failure causes (including
   * interruption of an inner effect that has been made interruptible).
   */
  final def uninterruptible(implicit trace: Trace): ZIO[R, E, A] =
    withRuntimeFlags(RuntimeFlags.disableInterruption)

  /**
   * Returns the effect resulting from mapping the success of this effect to
   * unit.
   */
  def unit(implicit trace: Trace): ZIO[R, E, Unit] =
    self.flatMap(ZIO.unitZIOFn)

  /**
   * Converts a `ZIO[R, Either[E, B], A]` into a `ZIO[R, E, Either[A, B]]`. The
   * inverse of `left`.
   */
  final def unleft[E1, B](implicit
    ev: E IsSubtypeOfError Either[E1, B],
    trace: Trace
  ): ZIO[R, E1, Either[A, B]] =
    self.foldZIO(
      e => ev(e).fold(e1 => ZIO.fail(e1), b => Exit.succeed(Right(b))),
      a => Exit.succeed(Left(a))
    )

  /**
   * The moral equivalent of `if (!p) Some(exp) else None`
   */
  final def unless(p: => Boolean)(implicit trace: Trace): ZIO[R, E, Option[A]] =
    ZIO.unless(p)(self)

  /**
   * The moral equivalent of `if (!p) { expr; () }`
   */
  final def unlessDiscard(p: => Boolean)(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.unlessDiscard(p)(self)

  /**
   * The moral equivalent of `if (!p) Some(exp) else None` when `p` has
   * side-effects
   */
  final def unlessZIO[R1 <: R, E1 >: E](p: => ZIO[R1, E1, Boolean])(implicit
    trace: Trace
  ): ZIO[R1, E1, Option[A]] =
    ZIO.unlessZIO(p)(self)

  /**
   * The moral equivalent of `if (!p) { expr; () }` when `p` has side-effects
   */
  final def unlessZIODiscard[R1 <: R, E1 >: E](p: => ZIO[R1, E1, Boolean])(implicit
    trace: Trace
  ): ZIO[R1, E1, Unit] =
    ZIO.unlessZIODiscard(p)(self)

  /**
   * Takes some fiber failures and converts them into errors.
   */
  final def unrefine[E1 >: E](pf: PartialFunction[Throwable, E1])(implicit trace: Trace): ZIO[R, E1, A] =
    unrefineWith(pf)(identity)

  /**
   * Takes some fiber failures and converts them into errors.
   */
  final def unrefineTo[E1 >: E: ClassTag](implicit trace: Trace): ZIO[R, E1, A] =
    unrefine { case e: E1 => e }

  /**
   * Takes some fiber failures and converts them into errors, using the
   * specified function to convert the `E` into an `E1`.
   */
  final def unrefineWith[E1](
    pf: PartialFunction[Throwable, E1]
  )(f: E => E1)(implicit trace: Trace): ZIO[R, E1, A] =
    catchAllCause { cause =>
      cause.find {
        case Cause.Die(t, _) if pf.isDefinedAt(t) => pf(t)
      }.fold(Exit.failCause(cause.map(f)))(ZIO.failFn)
    }

  /**
   * Converts a `ZIO[R, Either[B, E], A]` into a `ZIO[R, E, Either[B, A]]`. The
   * inverse of `right`.
   */
  final def unright[E1, B](implicit
    ev: E IsSubtypeOfError Either[B, E1],
    trace: Trace
  ): ZIO[R, E1, Either[B, A]] =
    self.foldZIO(
      e => ev(e).fold(b => Exit.succeed(Left(b)), e1 => Exit.fail(e1)),
      a => Exit.succeed(Right(a))
    )

  /**
   * Converts an option on errors into an option on values.
   */
  final def unsome[E1](implicit ev: E IsSubtypeOfError Option[E1], trace: Trace): ZIO[R, E1, Option[A]] =
    self.foldZIO(
      e => ev(e).fold[ZIO[R, E1, Option[A]]](Exit.none)(ZIO.failFn),
      a => Exit.succeed(Some(a))
    )

  /**
   * Updates a service in the environment of this effect.
   */
  final def updateService[M] =
    new ZIO.UpdateService[R, E, A, M](self)

  /**
   * Updates a service at the specified key in the environment of this effect.
   */
  final def updateServiceAt[Service]: ZIO.UpdateServiceAt[R, E, A, Service] =
    new ZIO.UpdateServiceAt[R, E, A, Service](self)

  final def unexit[E1 >: E, A2](implicit ev: A <:< Exit[E1, A2], trace: Trace): ZIO[R, E1, A2] =
    self.flatMap(exit => exit)

  /**
   * The inverse operation to `sandbox`. Submerges the full cause of failure.
   */
  final def unsandbox[E1](implicit ev: E IsSubtypeOfError Cause[E1], trace: Trace): ZIO[R, E1, A] =
    ZIO.unsandbox(self.mapError(ev))

  /**
   * Sequentially zips the this result with the specified result. Combines both
   * `Cause[E1]` when both effects fail.
   */
  final def validate[R1 <: R, E1 >: E, B](
    that: => ZIO[R1, E1, B]
  )(implicit zippable: Zippable[A, B], trace: Trace): ZIO[R1, E1, zippable.Out] =
    validateWith(that)(zippable.zip(_, _))

  /**
   * Returns an effect that executes both this effect and the specified effect,
   * in parallel. Combines both Cause[E1]` when both effects fail.
   */
  final def validatePar[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit
    trace: Trace
  ): ZIO[R1, E1, (A, B)] =
    validateWithPar(that)((_, _))

  /**
   * Sequentially zips this effect with the specified effect using the specified
   * combiner function. Combines the causes in case both effect fail.
   */
  final def validateWith[R1 <: R, E1 >: E, B, C](that: => ZIO[R1, E1, B])(f: (A, B) => C)(implicit
    trace: Trace
  ): ZIO[R1, E1, C] =
    self.exit.zipWith(that.exit)(_.zipWith(_)(f, _ ++ _)).unexit

  /**
   * Returns an effect that executes both this effect and the specified effect,
   * in parallel, combining their results with the specified `f` function. If
   * both sides fail, then the cause will be combined.
   */
  final def validateWithPar[R1 <: R, E1 >: E, B, C](that: => ZIO[R1, E1, B])(f: (A, B) => C)(implicit
    trace: Trace
  ): ZIO[R1, E1, C] =
    self.exit.zipWithPar(that.exit)(_.zipWith(_)(f, _ && _)).unexit

  /**
   * The moral equivalent of `if (p) Some(exp) else None`
   */
  final def when(p: => Boolean)(implicit trace: Trace): ZIO[R, E, Option[A]] =
    ZIO.when(p)(self)

  /**
   * The moral equivalent of `if (p) { expr; () }`
   */
  final def whenDiscard(p: => Boolean)(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.whenDiscard(p)(self)

  /**
   * Executes this workflow when value of the specified `FiberRef` satisfies the
   * predicate.
   */
  final def whenFiberRef[S](ref: => FiberRef[S])(f: S => Boolean)(implicit trace: Trace): ZIO[R, E, (S, Option[A])] =
    ref.getWith { s =>
      if (f(s)) self.map(a => (s, Some(a)))
      else Exit.succeed((s, None))
    }

  /**
   * Executes this workflow when the value of the `Ref` satisfies the predicate.
   */
  final def whenRef[S](ref: => Ref[S])(f: S => Boolean)(implicit trace: Trace): ZIO[R, E, (S, Option[A])] =
    ref.get.flatMap { s =>
      if (f(s)) self.map(a => (s, Some(a)))
      else Exit.succeed((s, None))
    }

  /**
   * The moral equivalent of `if (p) Some(exp) else None` when `p` has
   * side-effects
   */
  final def whenZIO[R1 <: R, E1 >: E](
    p: => ZIO[R1, E1, Boolean]
  )(implicit trace: Trace): ZIO[R1, E1, Option[A]] =
    ZIO.whenZIO(p)(self)

  /**
   * The moral equivalent of `if (p) { expr; () }` when `p` has side-effects
   */
  final def whenZIODiscard[R1 <: R, E1 >: E](
    p: => ZIO[R1, E1, Boolean]
  )(implicit trace: Trace): ZIO[R1, E1, Unit] =
    ZIO.whenZIODiscard(p)(self)

  /**
   * Executes this workflow with the specified implementation of the clock
   * service.
   */
  final def withClock[B <: Clock](clock: => B)(implicit tag: Tag[B], trace: Trace): ZIO[R, E, A] =
    ZIO.withClock(clock)(self)

  /**
   * Executes this workflow with the specified configuration provider.
   */
  final def withConfigProvider[B <: ConfigProvider](
    configProvider: => B
  )(implicit tag: Tag[B], trace: Trace): ZIO[R, E, A] =
    ZIO.withConfigProvider(configProvider)(self)

  /**
   * Executes this workflow with the specified implementation of the console
   * service.
   */
  final def withConsole[B <: Console](console: => B)(implicit tag: Tag[B], trace: Trace): ZIO[R, E, A] =
    ZIO.withConsole(console)(self)

  /**
   * Returns a new scoped workflow that returns the result of this workflow as
   * well as a finalizer that can be run to close the scope of this workflow.
   */
  final def withEarlyRelease(implicit trace: Trace): ZIO[R with Scope, E, (UIO[Unit], A)] =
    ZIO.scopeWith { parent =>
      parent.fork.flatMap { child =>
        child.extend[R](self).map { a =>
          (ZIO.fiberIdWith(fiberId => child.close(Exit.interrupt(fiberId))), a)
        }
      }
    }

  /**
   * Treats this effect as the acquisition of a resource and adds the specified
   * finalizer to the current scope. This effect will be run uninterruptibly and
   * the finalizer will be run when the scope is closed.
   */
  final def withFinalizer[R1 <: R](finalizer: A => URIO[R1, Any])(implicit
    trace: Trace
  ): ZIO[R1 with Scope, E, A] =
    ZIO.acquireRelease(self)(finalizer)

  /**
   * A more powerful variant of `withFinalizer` that allows the finalizer to
   * depend on the `Exit` value that the scope is closed with.
   */
  final def withFinalizerExit[R1 <: R](finalizer: (A, Exit[Any, Any]) => URIO[R1, Any])(implicit
    trace: Trace
  ): ZIO[R1 with Scope, E, A] =
    ZIO.acquireReleaseExit(self)(finalizer)

  /**
   * Executes this workflow with the specified logger added.
   */
  final def withLogger[B <: ZLogger[String, Any]](logger: => B)(implicit tag: Tag[B], trace: Trace): ZIO[R, E, A] =
    ZIO.withLogger(logger)(self)

  /**
   * Runs this effect with the specified maximum number of fibers for parallel
   * operators.
   */
  final def withParallelism(n: => Int)(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.withParallelism(n)(self)

  /**
   * Runs this effect with an unbounded maximum number of fibers for parallel
   * operators.
   */
  def withParallelismUnbounded(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.withParallelismUnbounded(self)

  /**
   * Executes this workflow with the specified implementation of the random
   * service.
   */
  final def withRandom[B <: Random](random: => B)(implicit tag: Tag[B], trace: Trace): ZIO[R, E, A] =
    ZIO.withRandom(random)(self)

  /**
   * Returns a new ZIO effect that will update the runtime flags according to
   * the specified patch within the scope of this ZIO effect.
   */
  final def withRuntimeFlags(patch: RuntimeFlags.Patch)(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.UpdateRuntimeFlagsWithin(trace, patch, _ => self)

  /**
   * Executes this workflow with the specified implementation of the system
   * service.
   */
  final def withSystem[B <: System](system: => B)(implicit tag: Tag[B], trace: Trace): ZIO[R, E, A] =
    ZIO.withSystem(system)(self)

  /**
   * A named alias for `<*>`.
   */
  final def zip[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit
    zippable: Zippable[A, B],
    trace: Trace
  ): ZIO[R1, E1, zippable.Out] =
    self.zipWith(that)((a, b) => zippable.zip(a, b))

  /**
   * A named alias for `<*`.
   */
  final def zipLeft[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, A] =
    self <* that

  /**
   * A named alias for `<&>`.
   */
  final def zipPar[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit
    zippable: Zippable[A, B],
    trace: Trace
  ): ZIO[R1, E1, zippable.Out] =
    self <&> that

  /**
   * A named alias for `<&`.
   */
  final def zipParLeft[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, A] =
    self <& that

  /**
   * A named alias for `&>`.
   */
  final def zipParRight[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
    self &> that

  /**
   * A named alias for `*>`.
   */
  final def zipRight[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
    self *> that

  /**
   * Sequentially zips this effect with the specified effect using the specified
   * combiner function.
   */
  final def zipWith[R1 <: R, E1 >: E, B, C](that: => ZIO[R1, E1, B])(f: (A, B) => C)(implicit
    trace: Trace
  ): ZIO[R1, E1, C] =
    self.flatMap(a => that.map(b => f(a, b)))

  /**
   * Returns an effect that executes both this effect and the specified effect,
   * in parallel, combining their results with the specified `f` function. If
   * either side fails, then the other side will be interrupted.
   */
  final def zipWithPar[R1 <: R, E1 >: E, B, C](
    that: => ZIO[R1, E1, B]
  )(f: (A, B) => C)(implicit trace: Trace): ZIO[R1, E1, C] =
    ZIO.uninterruptibleMask { restore =>
      val that0 = that
      if (self.isInstanceOf[Exit.Success[?]]) {
        self.zipWith(restore(that0))(f)
      } else if (that0.isInstanceOf[Exit.Success[?]]) {
        restore(self).zipWith(that0)(f)
      } else
        ZIO.withFiberRuntime[R1, E1, C] { (fiber, _) =>
          val promise     = Promise.unsafe.make[Unit, Boolean](FiberId.None)(Unsafe)
          val ref         = new java.util.concurrent.atomic.AtomicBoolean(false)
          val parentScope = fiber.scope

          def fork[R, E, A](zio: => ZIO[R, E, A], side: Boolean): ZIO[R, Nothing, Fiber[E, A]] =
            restore(zio)
              .foldCauseZIO(
                cause =>
                  ZIO.withFiberRuntime[Any, E, A] { (childFiber, _) =>
                    ZIO.suspendSucceed {
                      childFiber.transferChildren(parentScope)
                      promise.fail(()) *> Exit.failCause(cause)
                    }
                  },
                a =>
                  ZIO.withFiberRuntime[Any, Nothing, A] { (childFiber, _) =>
                    Exit.succeed {
                      childFiber.transferChildren(parentScope)
                      if (ref.getAndSet(true)) {
                        promise.unsafe.done(Exit.succeed(side))(Unsafe)
                      }
                      a
                    }
                  }
              )
              .forkDaemon

          fork(self, false).zip(fork(that0, true)).flatMap { case (left, right) =>
            restore(promise.await).foldCauseZIO(
              cause =>
                left.interruptFork *> right.interruptFork *>
                  left.await.zip(right.await).flatMap { case (left, right) =>
                    left.zipWith(right)(f, _ && _) match {
                      case Exit.Failure(causes) => Exit.failCause(cause.stripFailures && causes)
                      case _                    => Exit.failCause(cause.stripFailures)
                    }
                  },
              leftWins =>
                if (leftWins) left.join.zipWith(right.join)((a, b) => f(a, b))
                else right.join.zipWith(left.join)((b, a) => f(a, b))
            )
          }
        }
    }

  private[this] final def tryOrElse[R1 <: R, E2, B](
    that: => ZIO[R1, E2, B],
    success: A => ZIO[R1, E2, B]
  )(implicit trace: Trace): ZIO[R1, E2, B] =
    self.foldCauseZIO(
      cause =>
        cause.keepDefects match {
          case None    => that
          case Some(c) => Exit.failCause(c)
        },
      success
    )

  private[zio] def trace: Trace
}

object ZIO extends ZIOCompanionPlatformSpecific with ZIOCompanionVersionSpecific {
  private[zio] object unsafe {
    def fork[R, E1, E2, A, B](
      trace: Trace,
      effect: ZIO[R, E1, A],
      parentFiber: Fiber.Runtime[E2, B],
      parentRuntimeFlags: RuntimeFlags,
      overrideScope: FiberScope = null
    )(implicit unsafe: Unsafe): internal.FiberRuntime[E1, A] = {
      val childFiber = ZIO.unsafe.makeChildFiber(trace, effect, parentFiber, parentRuntimeFlags, overrideScope)

      childFiber.startConcurrently(effect)

      childFiber
    }

    def makeChildFiber[R, E1, E2, A, B](
      trace: Trace,
      effect: ZIO[R, E1, A],
      parentFiber: Fiber.Runtime[E2, B],
      parentRuntimeFlags: RuntimeFlags,
      overrideScope: FiberScope
    )(implicit unsafe: Unsafe): internal.FiberRuntime[E1, A] = {
      val parentFiberRefs = parentFiber.getFiberRefs()
      val childId         = FiberId.generate(parentFiberRefs)(trace)
      val childFiberRefs  = parentFiberRefs.forkAs(childId) // TODO: Optimize

      val childFiber = internal.FiberRuntime[E1, A](childId, childFiberRefs, parentRuntimeFlags)

      // Call the supervisor who can observe the fork of the child fiber
      val supervisor = childFiber.getSupervisor()

      if (supervisor ne Supervisor.none) {
        val childEnvironment = childFiberRefs.getOrDefault(FiberRef.currentEnvironment)
        supervisor.onStart(
          childEnvironment,
          effect.asInstanceOf[ZIO[Any, Any, Any]],
          Some(parentFiber),
          childFiber
        )

        childFiber.addObserver(exit => supervisor.onEnd(exit, childFiber))
      }

      val parentScope =
        if (overrideScope ne null) overrideScope
        else parentFiber.getFiberRef(FiberRef.forkScopeOverride).getOrElse(parentFiber.scope)

      parentScope.add(parentFiber, parentRuntimeFlags, childFiber)(trace, unsafe)

      childFiber
    }

  }

  /**
   * The level of parallelism for parallel operators.
   */
  final lazy val Parallelism: FiberRef[Option[Int]] =
    FiberRef.unsafe.make[Option[Int]](None)(Unsafe)

  /**
   * Submerges the error case of an `Either` into the `ZIO`. The inverse
   * operation of `IO.either`.
   */
  def absolve[R, E, A](v: => ZIO[R, E, Either[E, A]])(implicit trace: Trace): ZIO[R, E, A] =
    suspendSucceed(v).absolve

  /**
   * Constructs a scoped resource from an `acquire` and `release` effect. If
   * `acquire` successfully completes execution then `release` will be added to
   * the finalizers associated with the scope of this effect and is guaranteed
   * to be run when the scope is closed.
   *
   * The `acquire` and `release` effects will be run uninterruptibly.
   */
  def acquireRelease[R, R1, E, A](acquire: => ZIO[R, E, A])(release: A => ZIO[R1, Nothing, Any])(implicit
    trace: Trace
  ): ZIO[R with R1 with Scope, E, A] =
    acquireReleaseExit(acquire)((a, _) => release(a))

  /**
   * A more powerful variant of `acquireRelease` that allows the `release`
   * effect to depend on the `Exit` value specified when the scope is closed.
   */
  def acquireReleaseExit[R, R1, E, A](
    acquire: => ZIO[R, E, A]
  )(release: (A, Exit[Any, Any]) => ZIO[R1, Nothing, Any])(implicit
    trace: Trace
  ): ZIO[R with R1 with Scope, E, A] =
    ZIO.uninterruptible(acquire.tap(a => ZIO.addFinalizerExit(exit => release(a, exit))))

  /**
   * A variant of `acquireRelease` that allows the `acquire` effect to be
   * interruptible. Since the `acquire` effect could be interrupted after
   * partially acquiring resources, the `release` effect is not allowed to
   * access the resource produced by `acquire` and must independently determine
   * what finalization, if any, needs to be performed (e.g. by examining in
   * memory state).
   */
  def acquireReleaseInterruptible[R, R1, E, A](acquire: => ZIO[R, E, A])(release: ZIO[R1, Nothing, Any])(implicit
    trace: Trace
  ): ZIO[R with R1 with Scope, E, A] =
    acquireReleaseInterruptibleExit(acquire)(_ => release)

  /**
   * A more powerful variant of `acquireReleaseInterruptible` that allows the
   * `release` effect to depend on the `Exit` value specified when the scope is
   * closed.
   */
  def acquireReleaseInterruptibleExit[R, R1, E, A](acquire: => ZIO[R, E, A])(
    release: Exit[Any, Any] => ZIO[R1, Nothing, Any]
  )(implicit trace: Trace): ZIO[R with R1 with Scope, E, A] =
    ZIO.suspendSucceed(acquire.ensuring(ZIO.addFinalizerExit(release)))

  /**
   * Given an effect representing acquisition of a resource (for example,
   * opening a file, launching a thread, etc.), `acquireReleaseWith` can be used
   * to ensure the acquisition is not interrupted and the resource is always
   * released.
   *
   * The function does two things:
   *
   *   1. Ensures this `acquire` effect will not be interrupted. Of course,
   *      acquisition may fail for internal reasons (an uncaught exception).
   *
   *   1. Ensures the `release` effect will not be interrupted, and will be
   *      executed so long as this effect successfully acquires the resource.
   *
   * In between acquisition and release of the resource, the `use` effect is
   * executed.
   *
   * If the `release` effect fails, then the entire effect will fail even if the
   * `use` effect succeeds. If this fail-fast behavior is not desired, errors
   * produced by the `release` effect can be caught and ignored.
   *
   * {{{
   * ZIO.acquireReleaseWith(openFile("data.json"))(closeFile) { file =>
   *   for {
   *     header <- readHeader(file)
   *     ...
   *   } yield result
   * }
   * }}}
   */
  def acquireReleaseWith[R, E, A](acquire: => ZIO[R, E, A]): ZIO.Acquire[R, E, A] =
    new ZIO.Acquire[R, E, A](() => acquire)

  /**
   * Acquires a resource, uses the resource, and then releases the resource.
   * Neither the acquisition nor the release will be interrupted, and the
   * resource is guaranteed to be released, so long as the `acquire` effect
   * succeeds. If `use` fails, then after release, the returned effect will fail
   * with the same error.
   */
  def acquireReleaseExitWith[R, E, A](acquire: => ZIO[R, E, A]): ZIO.AcquireExit[R, E, A] =
    new ZIO.AcquireExit(() => acquire)

  /**
   * Adds a finalizer to the scope of this effect. The finalizer is guaranteed
   * to be run when the scope is closed.
   */
  def addFinalizer[R](finalizer: => URIO[R, Any])(implicit trace: Trace): ZIO[R with Scope, Nothing, Any] =
    addFinalizerExit(_ => finalizer)

  /**
   * A more powerful variant of `addFinalizer` that allows the finalizer to
   * depend on the `Exit` value that the scope is closed with.
   */
  def addFinalizerExit[R](
    finalizer: Exit[Any, Any] => URIO[R, Any]
  )(implicit trace: Trace): ZIO[R with Scope, Nothing, Any] =
    ZIO.environmentWithZIO[R] { environment =>
      ZIO.scopeWith { scope =>
        scope.addFinalizerExit(exit => finalizer(exit).provideEnvironment(environment))
      }
    }

  /**
   * Makes an explicit check to see if the fiber has been interrupted, and if
   * so, performs self-interruption
   */
  def allowInterrupt(implicit trace: Trace): UIO[Unit] =
    descriptorWith(d => if (d.interrupters.nonEmpty) interrupt else Exit.unit)

  def asyncInterruptUnsafe[R, E, A](
    register: Unsafe => (ZIO[R, E, A] => Unit) => Either[URIO[R, Any], ZIO[R, E, A]],
    blockingOn: => FiberId = FiberId.None
  )(implicit trace: Trace): ZIO[R, E, A] =
    asyncInterrupt(cb => register(Unsafe)(cb), blockingOn)

  /**
   * Converts an asynchronous, callback-style API into a ZIO effect, which will
   * be executed asynchronously.
   *
   * With this variant, the registration function may return a ZIO effect.
   */
  def asyncZIO[R, E, A](
    register: (ZIO[R, E, A] => Unit) => ZIO[R, E, Any]
  )(implicit trace: Trace): ZIO[R, E, A] =
    for {
      p <- Promise.make[E, A]
      r <- ZIO.runtime[R]
      a <- ZIO.uninterruptibleMask { restore =>
             val f = register(k => r.unsafe.fork(k.intoPromise(p))(trace, Unsafe))

             restore(f.catchAllCause(p.refailCause)).fork *> restore(p.await)
           }
    } yield a

  def attemptUnsafe[A](a: Unsafe => A)(implicit trace: Trace): Task[A] =
    ZIO.attempt(a(Unsafe))

  def attemptBlockingIOUnsafe[A](effect: Unsafe => A)(implicit trace: Trace): IO[IOException, A] =
    attemptBlockingIO(effect(Unsafe))

  /**
   * Returns a new effect that, when executed, will execute the original effect
   * on the blocking thread pool.
   */
  def blocking[R, E, A](zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    FiberRef.currentBlockingExecutor.getWith(zio.onExecutor(_))

  /**
   * Retrieves the executor for all blocking tasks.
   */
  def blockingExecutor(implicit trace: Trace): UIO[Executor] =
    FiberRef.currentBlockingExecutor.get

  /**
   * Checks the interrupt status, and produces the effect returned by the
   * specified callback.
   */
  def checkInterruptible[R, E, A](f: zio.InterruptStatus => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.withFiberRuntime[R, E, A] { (_, status) =>
      f(InterruptStatus.fromBoolean(RuntimeFlags.interruption(status.runtimeFlags)))
    }

  /**
   * Retrieves the `Clock` service for this workflow.
   */
  def clock(implicit trace: Trace): UIO[Clock] =
    ZIO.clockWith(ZIO.successFn)

  /**
   * Retrieves the `Clock` service for this workflow and uses it to run the
   * specified workflow.
   */
  def clockWith[R, E, A](f: Clock => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    DefaultServices.currentServices.getWith(services => f(services.get(Clock.tag)))

  /**
   * Evaluate each effect in the structure from left to right, collecting the
   * the successful values and discarding the empty cases. For a parallel
   * version, see `collectPar`.
   */
  def collect[R, E, A, B, Collection[+Element] <: Iterable[Element]](
    in: Collection[A]
  )(
    f: A => ZIO[R, Option[E], B]
  )(implicit bf: BuildFrom[Collection[A], B, Collection[B]], trace: Trace): ZIO[R, E, Collection[B]] =
    foreach[R, E, A, Option[B], Iterable](in)(a => f(a).unsome).map(_.flatten).map(bf.fromSpecific(in))

  /**
   * Evaluate each effect in the structure from left to right, collecting the
   * the successful values and discarding the empty cases. For a parallel
   * version, see `collectPar`.
   */
  def collect[R, E, Key, Key2, Value, Value2](
    map: Map[Key, Value]
  )(f: (Key, Value) => ZIO[R, Option[E], (Key2, Value2)])(implicit trace: Trace): ZIO[R, E, Map[Key2, Value2]] =
    foreach[R, E, (Key, Value), Option[(Key2, Value2)], Iterable](map)(f.tupled(_).unsome).map(_.flatten.toMap)

  /**
   * Evaluate each effect in the structure from left to right, and collect the
   * results. For a parallel version, see `collectAllPar`.
   */
  def collectAll[R, E, A, Collection[+Element] <: Iterable[Element]](
    in: Collection[ZIO[R, E, A]]
  )(implicit
    bf: BuildFrom[Collection[ZIO[R, E, A]], A, Collection[A]],
    trace: Trace
  ): ZIO[R, E, Collection[A]] =
    foreach(in)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure from left to right, and collect the
   * results. For a parallel version, see `collectAllPar`.
   */
  def collectAll[R, E, A](in: Set[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, Set[A]] =
    foreach(in)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure from left to right, and collect the
   * results. For a parallel version, see `collectAllPar`.
   */
  def collectAll[R, E, A: ClassTag](in: Array[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, Array[A]] =
    foreach(in)(ZIO.identityFn)

  /**
   * Evaluate effect if present, and return its result as `Option[A]`.
   */
  def collectAll[R, E, A](in: Option[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, Option[A]] =
    foreach(in)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure from left to right, and collect the
   * results. For a parallel version, see `collectAllPar`.
   */
  def collectAll[R, E, A](in: NonEmptyChunk[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, NonEmptyChunk[A]] =
    foreach(in)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure from left to right, and discard the
   * results. For a parallel version, see `collectAllParDiscard`.
   */
  def collectAllDiscard[R, E, A](in: => Iterable[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, Unit] =
    foreachDiscard(in)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure in parallel, and collect the results.
   * For a sequential version, see `collectAll`.
   */
  def collectAllPar[R, E, A, Collection[+Element] <: Iterable[Element]](
    as: Collection[ZIO[R, E, A]]
  )(implicit
    bf: BuildFrom[Collection[ZIO[R, E, A]], A, Collection[A]],
    trace: Trace
  ): ZIO[R, E, Collection[A]] =
    foreachPar(as)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure in parallel, and collect the results.
   * For a sequential version, see `collectAll`.
   */
  def collectAllPar[R, E, A](as: Set[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, Set[A]] =
    foreachPar(as)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure in parallel, and collect the results.
   * For a sequential version, see `collectAll`.
   */
  def collectAllPar[R, E, A: ClassTag](as: Array[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, Array[A]] =
    foreachPar(as)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure in parallel, and collect the results.
   * For a sequential version, see `collectAll`.
   */
  def collectAllPar[R, E, A](as: NonEmptyChunk[ZIO[R, E, A]])(implicit
    trace: Trace
  ): ZIO[R, E, NonEmptyChunk[A]] =
    foreachPar(as)(ZIO.identityFn)

  /**
   * Evaluate each effect in the structure in parallel, and discard the results.
   * For a sequential version, see `collectAllDiscard`.
   */
  def collectAllParDiscard[R, E, A](as: => Iterable[ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, Unit] =
    foreachParDiscard(as)(ZIO.identityFn)

  /**
   * Evaluate and run each effect in the structure and collect discarding failed
   * ones.
   */
  def collectAllSuccesses[R, E, A, Collection[+Element] <: Iterable[Element]](
    in: Collection[ZIO[R, E, A]]
  )(implicit bf: BuildFrom[Collection[ZIO[R, E, A]], A, Collection[A]], trace: Trace): URIO[R, Collection[A]] =
    collectAllWith(in.map(_.either)) { case Right(a) => a }.map(bf.fromSpecific(in))

  /**
   * Evaluate and run each effect in the structure in parallel, and collect
   * discarding failed ones.
   */
  def collectAllSuccessesPar[R, E, A, Collection[+Element] <: Iterable[Element]](
    in: Collection[ZIO[R, E, A]]
  )(implicit bf: BuildFrom[Collection[ZIO[R, E, A]], A, Collection[A]], trace: Trace): URIO[R, Collection[A]] =
    collectAllWithPar(in.map(_.either)) { case Right(a) => a }.map(bf.fromSpecific(in))

  /**
   * Evaluate each effect in the structure with `collectAll`, and collect the
   * results with given partial function.
   */
  def collectAllWith[R, E, A, B, Collection[+Element] <: Iterable[Element]](in: Collection[ZIO[R, E, A]])(
    f: PartialFunction[A, B]
  )(implicit
    bf: BuildFrom[Collection[ZIO[R, E, A]], B, Collection[B]],
    trace: Trace
  ): ZIO[R, E, Collection[B]] =
    ZIO.collectAll[R, E, A, Iterable](in).map(_.collect(f)).map(bf.fromSpecific(in))

  /**
   * Evaluate each effect in the structure with `collectAllPar`, and collect the
   * results with given partial function.
   */
  def collectAllWithPar[R, E, A, U, Collection[+Element] <: Iterable[Element]](in: Collection[ZIO[R, E, A]])(
    f: PartialFunction[A, U]
  )(implicit
    bf: BuildFrom[Collection[ZIO[R, E, A]], U, Collection[U]],
    trace: Trace
  ): ZIO[R, E, Collection[U]] =
    ZIO.collectAllPar[R, E, A, Iterable](in).map(_.collect(f)).map(bf.fromSpecific(in))

  /**
   * Collects the first element of the `Iterable[A]` for which the effectual
   * function `f` returns `Some`.
   */
  def collectFirst[R, E, A, B](
    as: => Iterable[A]
  )(f: A => ZIO[R, E, Option[B]])(implicit trace: Trace): ZIO[R, E, Option[B]] =
    succeed(as.iterator).flatMap { iterator =>
      def loop: ZIO[R, E, Option[B]] =
        if (iterator.hasNext) f(iterator.next()).flatMap(_.fold(loop)(some(_)))
        else none
      loop
    }

  /**
   * Evaluate each effect in the structure in parallel, collecting the the
   * successful values and discarding the empty cases.
   */
  def collectPar[R, E, A, B, Collection[+Element] <: Iterable[Element]](
    in: Collection[A]
  )(
    f: A => ZIO[R, Option[E], B]
  )(implicit bf: BuildFrom[Collection[A], B, Collection[B]], trace: Trace): ZIO[R, E, Collection[B]] =
    foreachPar[R, E, A, Option[B], Iterable](in)(a => f(a).unsome).map(_.flatten).map(bf.fromSpecific(in))

  /**
   * Evaluate each effect in the structure from left to right, collecting the
   * the successful values and discarding the empty cases. For a parallel
   * version, see `collectPar`.
   */
  def collectPar[R, E, Key, Key2, Value, Value2](
    map: Map[Key, Value]
  )(f: (Key, Value) => ZIO[R, Option[E], (Key2, Value2)])(implicit trace: Trace): ZIO[R, E, Map[Key2, Value2]] =
    foreachPar[R, E, (Key, Value), Option[(Key2, Value2)], Iterable](map)(f.tupled(_).unsome).map(_.flatten.toMap)

  /**
   * Similar to Either.cond, evaluate the predicate, return the given A as
   * success if predicate returns true, and the given E as error otherwise
   *
   * For effectful conditionals, see [[ZIO.ifZIO]]
   */
  def cond[E, A](predicate: => Boolean, result: => A, error: => E)(implicit trace: Trace): IO[E, A] =
    ZIO.suspendSucceed(if (predicate) ZIO.succeed(result) else ZIO.fail(error))

  /**
   * Uses the current config provider to load the specified config, or fail with
   * an error of type Config.Error.
   */
  def config[A](config: Config[A])(implicit trace: Trace): ZIO[Any, Config.Error, A] =
    ZIO.configProviderWith(_.load(config))

  /**
   * Uses the current config provider to load the config of type `A`, or fail
   * with an error of type Config.Error.
   */
  def config[A](implicit trace: Trace, config: Config[A]): ZIO[Any, Config.Error, A] =
    ZIO.configProviderWith(_.load[A])

  /**
   * Retrieves the current config provider, and passes it to the specified
   * function, which may return an effect that uses the provider to perform some
   * work or compute some value.
   */
  def configProviderWith[R, E, A](f: ConfigProvider => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    DefaultServices.currentServices.getWith(services => f(services.get(ConfigProvider.tag)))

  /**
   * Retrieves the `Console` service for this workflow.
   */
  def console(implicit trace: Trace): UIO[Console] =
    ZIO.consoleWith(ZIO.successFn)

  /**
   * Retrieves the `Console` service for this workflow and uses it to run the
   * specified workflow.
   */
  def consoleWith[R, E, A](f: Console => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    DefaultServices.currentServices.getWith(services => f(services.get(Console.tag)))

  /**
   * Prints the specified message to the console for debugging purposes.
   */
  def debug(value: => Any)(implicit trace: Trace): UIO[Unit] =
    ZIO.succeed(println(value))

  /**
   * Returns information about the current fiber, such as its identity.
   */
  def descriptor(implicit trace: Trace): UIO[Fiber.Descriptor] =
    descriptorWith(ZIO.successFn)

  /**
   * Constructs an effect based on information about the current fiber, such as
   * its identity.
   */
  def descriptorWith[R, E, A](f: Fiber.Descriptor => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.withFiberRuntime[R, E, A] { (fiberState, status) =>
      val descriptor =
        Fiber.Descriptor(
          fiberState.id,
          status,
          fiberState.getFiberRef(FiberRef.interruptedCause).interruptors,
          fiberState.getCurrentExecutor(),
          isLocked = RuntimeFlags.eagerShiftBack(status.runtimeFlags) ||
            fiberState.getFiberRef(FiberRef.overrideExecutor).isDefined
        )

      f(descriptor)
    }

  /**
   * Returns an effect that dies with the specified `Throwable`. This method can
   * be used for terminating a fiber because a defect has been detected in the
   * code.
   */
  def die(t: => Throwable)(implicit trace: Trace): UIO[Nothing] =
    failCause(Cause.Die(t, StackTrace.none))

  /**
   * Returns an effect that dies with a [[java.lang.RuntimeException]] having
   * the specified text message. This method can be used for terminating a fiber
   * because a defect has been detected in the code.
   */
  def dieMessage(message: => String)(implicit trace: Trace): UIO[Nothing] =
    failCause(Cause.stackless(Cause.die(new RuntimeException(message))))

  /**
   * Returns an effect from a [[zio.Exit]] value.
   */
  def done[E, A](r: => Exit[E, A])(implicit trace: Trace): IO[E, A] =
    ZIO.suspendSucceed(r)

  /**
   * Accesses the whole environment of the effect.
   */
  def environment[R](implicit trace: Trace): URIO[R, ZEnvironment[R]] =
    FiberRef.currentEnvironment.get.asInstanceOf[URIO[R, ZEnvironment[R]]]

  /**
   * Accesses the environment of the effect.
   */
  def environmentWith[R]: ZIO.EnvironmentWithPartiallyApplied[R] =
    new ZIO.EnvironmentWithPartiallyApplied[R]

  /**
   * Effectually accesses the environment of the effect.
   */
  def environmentWithZIO[R]: ZIO.EnvironmentWithZIOPartiallyApplied[R] =
    new ZIO.EnvironmentWithZIOPartiallyApplied[R]

  /**
   * Retrieves the executor for this effect.
   */
  def executor(implicit trace: Trace): UIO[Executor] =
    ZIO.executorWith(ZIO.successFn)

  /**
   * Constructs an effect based on the current executor.
   */
  def executorWith[R, E, A](f: Executor => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.withFiberRuntime[R, E, A]((fiberState, _) => f(fiberState.getCurrentExecutor()))

  /**
   * Determines whether any element of the `Iterable[A]` satisfies the effectual
   * predicate `f`.
   */
  def exists[R, E, A](
    as: => Iterable[A]
  )(f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Boolean] =
    succeed(as.iterator).flatMap { iterator =>
      def loop: ZIO[R, E, Boolean] =
        if (iterator.hasNext) f(iterator.next()).flatMap(b => if (b) Exit.`true` else loop)
        else Exit.`false`
      loop
    }

  /**
   * Returns an effect that models failure with the specified error. The moral
   * equivalent of `throw` for pure code.
   */
  def fail[E](error: => E)(implicit trace: Trace): IO[E, Nothing] =
    failCause(Cause.fail(error))

  /**
   * Returns an effect that models failure with the specified `Cause`.
   */
  def failCause[E](cause: => Cause[E])(implicit trace0: Trace): IO[E, Nothing] =
    ZIO.stackTrace(trace0).flatMap { trace =>
      FiberRef.currentLogSpan.getWith { spans =>
        FiberRef.currentLogAnnotations.getWith { annotations =>
          Exit.failCause(cause.applyAll(trace, spans, annotations))
        }
      }
    }

  /**
   * Returns the `FiberId` of the fiber executing the effect that calls this
   * method.
   */
  def fiberId(implicit trace: Trace): UIO[FiberId.Runtime] =
    fiberIdWith(ZIO.successFn)

  /**
   * Constructs an effect based on the `FiberId` of the fiber executing the
   * effect that calls this method.
   */
  def fiberIdWith[R, E, A](f: FiberId.Runtime => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.withFiberRuntime[R, E, A]((fiberState, _) => f(fiberState.id))

  /**
   * Filters the collection using the specified effectual predicate.
   */
  def filter[R, E, A, Collection[+Element] <: Iterable[Element]](
    as: Collection[A]
  )(
    f: A => ZIO[R, E, Boolean]
  )(implicit bf: BuildFrom[Collection[A], A, Collection[A]], trace: Trace): ZIO[R, E, Collection[A]] =
    ZIO
      .foldLeft(as)(bf.newBuilder(as)) { (builder, a) =>
        f(a).map(b => if (b) builder += a else builder)
      }
      .map(_.result())

  /**
   * Filters the Set[A] using the specified effectual predicate.
   */
  def filter[R, E, A](as: Set[A])(f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Set[A]] =
    filter[R, E, A, Iterable](as)(f).map(_.toSet)

  /**
   * Filters the collection in parallel using the specified effectual predicate.
   *
   * See [[[zio.ZIO.filter[R,E,A,Collection*]]] for a sequential version.
   */
  def filterPar[R, E, A, Collection[+Element] <: Iterable[Element]](
    as: Collection[A]
  )(
    f: A => ZIO[R, E, Boolean]
  )(implicit bf: BuildFrom[Collection[A], A, Collection[A]], trace: Trace): ZIO[R, E, Collection[A]] =
    ZIO
      .foreachPar[R, E, A, Option[A], Iterable](as)(a => f(a).map(if (_) Some(a) else None))
      .map(_.flatten)
      .map(bf.fromSpecific(as))

  /**
   * Filters the Set[A] in parallel using the specified effectual predicate.
   *
   * See [[[zio.ZIO.filter[R,E,A,Collection*]]] for a sequential version.
   */
  def filterPar[R, E, A](as: Set[A])(f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Set[A]] =
    filterPar[R, E, A, Iterable](as)(f).map(_.toSet)

  /**
   * Filters the collection using the specified effectual predicate, removing
   * all elements that satisfy the predicate.
   */
  def filterNot[R, E, A, Collection[+Element] <: Iterable[Element]](
    as: Collection[A]
  )(
    f: A => ZIO[R, E, Boolean]
  )(implicit bf: BuildFrom[Collection[A], A, Collection[A]], trace: Trace): ZIO[R, E, Collection[A]] =
    filter(as)(f(_).map(!_))

  /**
   * Filters the Set[A] using the specified effectual predicate, removing all
   * elements that satisfy the predicate.
   */
  def filterNot[R, E, A](as: Set[A])(f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Set[A]] =
    filterNot[R, E, A, Iterable](as)(f).map(_.toSet)

  /**
   * Filters the collection in parallel using the specified effectual predicate,
   * emoving all elements that satisfy the predicate.
   *
   * See [[[zio.ZIO.filterNot[R,E,A,Collection*]]] for a sequential version.
   */
  def filterNotPar[R, E, A, Collection[+Element] <: Iterable[Element]](
    as: Collection[A]
  )(
    f: A => ZIO[R, E, Boolean]
  )(implicit bf: BuildFrom[Collection[A], A, Collection[A]], trace: Trace): ZIO[R, E, Collection[A]] =
    filterPar(as)(f(_).map(!_))

  /**
   * Filters the Set[A] in parallel using the specified effectual predicate,
   * removing all elements that satisfy the predicate.
   *
   * See [[[zio.ZIO.filterNot[R,E,A](as:Set*]]] for a sequential version.
   */
  def filterNotPar[R, E, A](as: Set[A])(f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Set[A]] =
    filterNotPar[R, E, A, Iterable](as)(f).map(_.toSet)

  /**
   * Returns an effect that runs the first effect and in case of failure, runs
   * each of the specified effects in order until one of them succeeds.
   */
  def firstSuccessOf[R, R1 <: R, E, A](
    zio: => ZIO[R, E, A],
    rest: => Iterable[ZIO[R1, E, A]]
  )(implicit trace: Trace): ZIO[R1, E, A] =
    ZIO.suspendSucceed {
      val iterator = rest.iterator

      def loop(zio: ZIO[R1, E, A]): ZIO[R1, E, A] =
        zio.catchAllCause { cause =>
          cause.failureOrCause.fold(
            _ => if (iterator.hasNext) loop(iterator.next()) else Exit.failCause(cause),
            cause => Exit.failCause(cause)
          )
        }

      loop(zio)
    }

  /**
   * Returns an effect that first executes the outer effect, and then executes
   * the inner effect, returning the value from the inner effect, and
   * effectively flattening a nested effect.
   */
  def flatten[R, E, A](zio: => ZIO[R, E, ZIO[R, E, A]])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed(zio.flatMap(ZIO.identityFn))

  /**
   * Folds an Iterable[A] using an effectual function f, working sequentially
   * from left to right.
   */
  def foldLeft[R, E, S, A](
    in: => Iterable[A]
  )(zero: => S)(f: (S, A) => ZIO[R, E, S])(implicit trace: Trace): ZIO[R, E, S] =
    ZIO.suspendSucceed {
      val iterator = in.iterator
      var s        = zero

      ZIO.whileLoop(iterator.hasNext)(f(s, iterator.next()))(s = _).as(s)
    }

  /**
   * Folds an Iterable[A] using an effectual function f, working sequentially
   * from right to left.
   */
  def foldRight[R, E, S, A](
    in: => Iterable[A]
  )(zero: => S)(f: (A, S) => ZIO[R, E, S])(implicit trace: Trace): ZIO[R, E, S] =
    foldLeft(in.toSeq.reverse)(zero)((s, a) => f(a, s))

  /**
   * Determines whether all elements of the `Iterable[A]` satisfy the effectual
   * predicate `f`.
   */
  def forall[R, E, A](
    as: => Iterable[A]
  )(f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Boolean] =
    succeed(as.iterator).flatMap { iterator =>
      def loop: ZIO[R, E, Boolean] =
        if (iterator.hasNext) f(iterator.next()).flatMap(b => if (b) loop else Exit.`false`)
        else Exit.`true`
      loop
    }

  /**
   * Applies the function `f` to each element of the `Collection[A]` and returns
   * the results in a new `Collection[B]`.
   *
   * For a parallel version of this method, see `foreachPar`. If you do not need
   * the results, see `foreachDiscard` for a more efficient implementation.
   */
  def foreach[R, E, A, B, Collection[+Element] <: Iterable[Element]](in: Collection[A])(
    f: A => ZIO[R, E, B]
  )(implicit bf: BuildFrom[Collection[A], B, Collection[B]], trace: Trace): ZIO[R, E, Collection[B]] =
    if (in.isEmpty) ZIO.succeed(bf.fromSpecific(in)(Nil))
    else
      ZIO.suspendSucceed {
        val iterator = in.iterator
        val builder  = bf.newBuilder(in)

        ZIO.whileLoop(iterator.hasNext)(f(iterator.next()))(builder += _).as(builder.result())
      }

  /**
   * Applies the function `f` to each element of the `Set[A]` and returns the
   * results in a new `Set[B]`.
   *
   * For a parallel version of this method, see `foreachPar`. If you do not need
   * the results, see `foreachDiscard` for a more efficient implementation.
   */
  final def foreach[R, E, A, B](in: Set[A])(f: A => ZIO[R, E, B])(implicit trace: Trace): ZIO[R, E, Set[B]] =
    if (in.isEmpty) ZIO.succeed(Set.empty)
    else
      ZIO.suspendSucceed {
        val iterator = in.iterator
        val builder  = Set.newBuilder[B]

        ZIO.whileLoop(iterator.hasNext)(f(iterator.next()))(builder += _).as(builder.result())
      }

  /**
   * Applies the function `f` to each element of the `Array[A]` and returns the
   * results in a new `Array[B]`.
   *
   * For a parallel version of this method, see `foreachPar`. If you do not need
   * the results, see `foreachDiscard` for a more efficient implementation.
   */
  final def foreach[R, E, A, B: ClassTag](in: Array[A])(f: A => ZIO[R, E, B])(implicit
    trace: Trace
  ): ZIO[R, E, Array[B]] =
    if (in.isEmpty) ZIO.succeed(Array.empty[B])
    else
      ZIO.suspendSucceed {
        val iterator = in.iterator
        val builder  = Array.newBuilder[B]
        builder.sizeHint(in.length)

        ZIO.whileLoop(iterator.hasNext)(f(iterator.next()))(builder += _).as(builder.result())
      }

  /**
   * Applies the function `f` to each element of the `Map[Key, Value]` and
   * returns the results in a new `Map[Key2, Value2]`.
   *
   * For a parallel version of this method, see `foreachPar`. If you do not need
   * the results, see `foreachDiscard` for a more efficient implementation.
   */
  def foreach[R, E, Key, Key2, Value, Value2](
    map: Map[Key, Value]
  )(f: (Key, Value) => ZIO[R, E, (Key2, Value2)])(implicit trace: Trace): ZIO[R, E, Map[Key2, Value2]] =
    if (map.isEmpty) ZIO.succeed(Map.empty)
    else
      ZIO.suspendSucceed {
        val iterator = map.iterator
        val builder  = Map.newBuilder[Key2, Value2]

        val ft = f.tupled
        ZIO.whileLoop(iterator.hasNext)(ft(iterator.next()))(builder += _).as(builder.result())
      }

  /**
   * Applies the function `f` if the argument is non-empty and returns the
   * results in a new `Option[B]`.
   */
  final def foreach[R, E, A, B](in: Option[A])(f: A => ZIO[R, E, B])(implicit
    trace: Trace
  ): ZIO[R, E, Option[B]] =
    in.fold[ZIO[R, E, Option[B]]](none)(f(_).map(Some(_)))

  /**
   * Applies the function `f` to each element of the `NonEmptyChunk[A]` and
   * returns the results in a new `NonEmptyChunk[B]`.
   *
   * For a parallel version of this method, see `foreachPar`. If you do not need
   * the results, see `foreachDiscard` for a more efficient implementation.
   */
  final def foreach[R, E, A, B](in: NonEmptyChunk[A])(f: A => ZIO[R, E, B])(implicit
    trace: Trace
  ): ZIO[R, E, NonEmptyChunk[B]] =
    in.mapZIO(f)

  /**
   * Applies the function `f` to each element of the `Iterable[A]` and runs
   * produced effects sequentially.
   *
   * Equivalent to `foreach(as)(f).unit`, but without the cost of building the
   * list of results.
   */
  def foreachDiscard[R, E, A](
    as: => Iterable[A]
  )(f: A => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.suspendSucceed {
      val as0 = as
      if (as0.isEmpty) Exit.unit
      else {
        val iterator = as0.iterator
        ZIO.whileLoop(iterator.hasNext)(f(iterator.next()))(_ => ())
      }
    }

  /**
   * Applies the function `f` to each element of the `Collection[A]` and returns
   * the result in a new `Collection[B]` using the specified execution strategy.
   */
  final def foreachExec[R, E, A, B, Collection[+Element] <: Iterable[Element]](as: Collection[A])(
    exec: => ExecutionStrategy
  )(
    f: A => ZIO[R, E, B]
  )(implicit bf: BuildFrom[Collection[A], B, Collection[B]], trace: Trace): ZIO[R, E, Collection[B]] =
    if (as.isEmpty) ZIO.succeed(bf.fromSpecific(as)(Nil))
    else
      ZIO.suspendSucceed {
        exec match {
          case ExecutionStrategy.Parallel =>
            ZIO.withParallelismUnboundedMask(restore => ZIO.foreachPar(as)(a => restore(f(a))))
          case ExecutionStrategy.ParallelN(n) =>
            ZIO.withParallelismMask(n)(restore => ZIO.foreachPar(as)(a => restore(f(a))))
          case ExecutionStrategy.Sequential =>
            ZIO.foreach(as)(f)
        }
      }

  /**
   * Applies the function `f` to each element of the `Collection[A]` in
   * parallel, and returns the results in a new `Collection[B]`.
   *
   * For a sequential version of this method, see `foreach`.
   */
  def foreachPar[R, E, A, B, Collection[+Element] <: Iterable[Element]](
    as: Collection[A]
  )(
    f: A => ZIO[R, E, B]
  )(implicit bf: BuildFrom[Collection[A], B, Collection[B]], trace: Trace): ZIO[R, E, Collection[B]] =
    if (as.isEmpty) ZIO.succeed(bf.fromSpecific(as)(Nil))
    else
      ZIO.parallelismWith {
        case Some(n) => foreachPar(n)(as)(f)
        case None    => foreachParUnbounded(as)(f)
      }

  /**
   * Applies the function `f` to each element of the `Set[A]` in parallel, and
   * returns the results in a new `Set[B]`.
   *
   * For a sequential version of this method, see `foreach`.
   */
  final def foreachPar[R, E, A, B](as: Set[A])(fn: A => ZIO[R, E, B])(implicit
    trace: Trace
  ): ZIO[R, E, Set[B]] =
    foreachPar[R, E, A, B, Iterable](as)(fn).map(_.toSet)

  /**
   * Applies the function `f` to each element of the `Array[A]` in parallel, and
   * returns the results in a new `Array[B]`.
   *
   * For a sequential version of this method, see `foreach`.
   */
  final def foreachPar[R, E, A, B: ClassTag](as: Array[A])(f: A => ZIO[R, E, B])(implicit
    trace: Trace
  ): ZIO[R, E, Array[B]] =
    foreachPar[R, E, A, B, Iterable](as)(f).map(_.toArray)

  /**
   * Applies the function `f` to each element of the `Map[Key, Value]` in
   * parallel and returns the results in a new `Map[Key2, Value2]`.
   *
   * For a sequential version of this method, see `foreach`.
   */
  def foreachPar[R, E, Key, Key2, Value, Value2](
    map: Map[Key, Value]
  )(f: (Key, Value) => ZIO[R, E, (Key2, Value2)])(implicit trace: Trace): ZIO[R, E, Map[Key2, Value2]] =
    foreachPar[R, E, (Key, Value), (Key2, Value2), Iterable](map)(f.tupled).map(_.toMap)

  /**
   * Applies the function `f` to each element of the `NonEmptyChunk[A]` in
   * parallel, and returns the results in a new `NonEmptyChunk[B]`.
   *
   * For a sequential version of this method, see `foreach`.
   */
  final def foreachPar[R, E, A, B](as: NonEmptyChunk[A])(fn: A => ZIO[R, E, B])(implicit
    trace: Trace
  ): ZIO[R, E, NonEmptyChunk[B]] =
    as.mapZIOPar(fn)

  /**
   * Applies the function `f` to each element of the `Iterable[A]` and runs
   * produced effects in parallel, discarding the results.
   *
   * For a sequential version of this method, see `foreachDiscard`.
   *
   * Optimized to avoid keeping full tree of effects, so that method could be
   * able to handle large input sequences. Behaves almost like this code:
   *
   * {{{
   * as.foldLeft(ZIO.unit) { (acc, a) => acc.zipParLeft(f(a)) }
   * }}}
   *
   * Additionally, interrupts all effects on any failure.
   */
  def foreachParDiscard[R, E, A](
    as: => Iterable[A]
  )(f: A => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.parallelismWith {
      case Some(n) => foreachParDiscard(n)(as)(f)
      case None    => foreachParUnboundedDiscard(as)(f)
    }

  /**
   * Returns an effect that forks all of the specified values, and returns a
   * composite fiber that produces a list of their results, in order.
   */
  def forkAll[R, E, A, Collection[+Element] <: Iterable[Element]](
    as: Collection[ZIO[R, E, A]]
  )(implicit
    bf: BuildFrom[Collection[ZIO[R, E, A]], A, Collection[A]],
    trace: Trace
  ): URIO[R, Fiber[E, Collection[A]]] =
    ZIO
      .foreach[R, Nothing, ZIO[R, E, A], Fiber[E, A], Iterable](as)(_.fork)
      .map(Fiber.collectAll[E, A, Iterable](_).map(bf.fromSpecific(as)))

  /**
   * Returns an effect that forks all of the specified values, and returns a
   * composite fiber that produces unit. This version is faster than [[forkAll]]
   * in cases where the results of the forked fibers are not needed.
   */
  def forkAllDiscard[R, E, A](as: => Iterable[ZIO[R, E, A]])(implicit trace: Trace): URIO[R, Fiber[E, Unit]] =
    ZIO
      .foreach[R, Nothing, ZIO[R, E, A], Fiber[E, A], Iterable](as)(_.fork)
      .map(Fiber.collectAllDiscard(_))

  /**
   * Constructs a `ZIO` value of the appropriate type for the specified input.
   */
  def from[Input](input: => Input)(implicit
    constructor: ZIOConstructor[Nothing, Any, Input],
    trace: Trace
  ): ZIO[constructor.OutEnvironment, constructor.OutError, constructor.OutSuccess] =
    constructor.make(input)

  def fromAutoCloseable[R, E, A <: AutoCloseable](fa: => ZIO[R, E, A])(implicit
    trace: Trace
  ): ZIO[R with Scope, E, A] =
    acquireRelease(fa)(a => if (a eq null) Exit.unit else ZIO.succeed(a.close()))

  /**
   * Lifts an `Either` into a `ZIO` value.
   */
  def fromEither[E, A](v: => Either[E, A])(implicit trace: Trace): IO[E, A] =
    succeed(v).flatMap(_.fold(ZIO.failFn, ZIO.successFn))

  /**
   * Lifts an `Either` into a `ZIO` value.
   */
  def fromEitherCause[E, A](v: => Either[Cause[E], A])(implicit trace: Trace): IO[E, A] =
    succeed(v).flatMap(_.fold(Exit.failCause, ZIO.successFn))

  /**
   * Creates a `ZIO` value that represents the exit value of the specified
   * fiber.
   */
  def fromFiber[E, A](fiber: => Fiber[E, A])(implicit trace: Trace): IO[E, A] =
    succeed(fiber).flatMap(_.join)

  /**
   * Creates a `ZIO` value that represents the exit value of the specified
   * fiber.
   */
  def fromFiberZIO[R, E, A](fiber: => ZIO[R, E, Fiber[E, A]])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed(fiber.flatMap(_.join))

  /**
   * Returns an effect that, when executed, will both create and launch a
   * [[scala.concurrent.Future]], feeding it an
   * [[scala.concurrent.ExecutionContext]] that is backed by ZIO's own executor.
   */
  def fromFuture[A](make: ExecutionContext => scala.concurrent.Future[A])(implicit trace: Trace): Task[A] =
    ZIO.executorWith { executor =>
      ZIO.suspend {
        import scala.util.{Success, Failure}

        val ec = executor.asExecutionContext
        val f  = make(ec)
        f.value match {
          case None =>
            ZIO.asyncInterrupt { (k: Task[A] => Unit) =>
              f.onComplete {
                case Success(a) => k(Exit.succeed(a))
                case Failure(t) => k(ZIO.fail(t))
              }(ec)

              Left {
                f match {
                  case cf: CancelableFuture[A] =>
                    ZIO.suspendSucceedUnsafe(implicit u =>
                      if (cf.isCompleted) Exit.unit else ZIO.fromFutureNow(cf.cancel()).ignore
                    )
                  case _ =>
                    Exit.unit
                }
              }
            }
          case Some(outcome) => outcome.fold(ZIO.fail(_), ZIO.successFn)
        }
      }
    }

  /**
   * Similar to [[fromFuture]], but doesn't attempt to suspend the future. Use
   * only when the future is already suspended by another effect
   *
   * @see
   *   Overloaded method that allows using the ZIO executor-backed execution
   *   context
   */
  private[zio] def fromFutureNow[A](f: concurrent.Future[A])(implicit trace: Trace, unsafe: Unsafe): Task[A] = {
    import scala.util.{Success, Failure}
    f.value match {
      case None =>
        ZIO.executorWith { executor =>
          val ec = executor.asExecutionContext
          ZIO.asyncInterrupt { (k: Task[A] => Unit) =>
            f.onComplete {
              case Success(a) => k(Exit.succeed(a))
              case Failure(t) => k(ZIO.fail(t))
            }(ec)
            Left {
              f match {
                case cf: CancelableFuture[A] =>
                  ZIO.suspendSucceed(if (cf.isCompleted) Exit.unit else ZIO.fromFutureNow(cf.cancel()).ignore)
                case _ => Exit.unit
              }
            }
          }
        }
      case Some(outcome) => outcome.fold(ZIO.fail(_), ZIO.successFn)
    }
  }

  /**
   * Imports a [[scala.concurrent.Promise]] we generate a future from promise,
   * and we pass to [fromFuture] to transform into Task[A]
   */
  def fromPromiseScala[A](promise: => scala.concurrent.Promise[A])(implicit trace: Trace): Task[A] =
    ZIO.suspend(ZIO.fromFutureNow(promise.future)(trace, Unsafe))

  /**
   * Imports a function that creates a [[scala.concurrent.Future]] from an
   * [[scala.concurrent.ExecutionContext]] into a `ZIO`. The provided
   * `ExecutionContext` will interrupt the `Future` between asynchronous
   * operations such as `map` and `flatMap` if this effect is interrupted. Note
   * that no attempt will be made to interrupt a `Future` blocking on a
   * synchronous operation and that the `Future` must be created using the
   * provided `ExecutionContext`.
   */
  def fromFutureInterrupt[A](
    make: ExecutionContext => scala.concurrent.Future[A]
  )(implicit trace: Trace): Task[A] =
    ZIO.executorWith { executor =>
      import scala.util.{Failure, Success}
      val interrupted = new java.util.concurrent.atomic.AtomicBoolean(false)
      val latch       = scala.concurrent.Promise[Unit]()
      ZIO.suspend {
        val ec = executor.asExecutionContext
        val interruptibleEC = new scala.concurrent.ExecutionContext {
          def execute(runnable: Runnable): Unit =
            if (!interrupted.get) ec.execute(runnable)
            else latch.success(())
          def reportFailure(cause: Throwable): Unit =
            ec.reportFailure(cause)
        }
        val f = make(interruptibleEC)
        f.value match {
          case None =>
            ZIO.async { (cb: Task[A] => Any) =>
              f.onComplete {
                case Success(a) => latch.success(()); cb(Exit.succeed(a))
                case Failure(t) => latch.success(()); cb(ZIO.fail(t))
              }(interruptibleEC)
            }
          case Some(outcome) => outcome.fold(ZIO.fail(_), ZIO.successFn)
        }
      }.onInterrupt(
        ZIO.suspendSucceedUnsafe { implicit u =>
          interrupted.set(true)
          ZIO.fromFutureNow(latch.future).orDie
        }
      )
    }

  /**
   * Lifts an `Option` into a `ZIO` but preserves the error as an option in the
   * error channel, making it easier to compose in some scenarios.
   */
  def fromOption[A](v: => Option[A])(implicit trace: Trace): IO[Option[Nothing], A] =
    succeed(v).flatMap(_.fold[IO[Option[Nothing], A]](fail(None))(ZIO.successFn))

  /**
   * Lifts a `Try` into a `ZIO`.
   */
  def fromTry[A](value: => scala.util.Try[A])(implicit trace: Trace): Task[A] =
    attempt(value).flatMap {
      case scala.util.Success(v) => Exit.succeed(v)
      case scala.util.Failure(t) => ZIO.fail(t)
    }

  /**
   * Returns a collection of all `FiberRef` values for the fiber running this
   * effect.
   */
  def getFiberRefs(implicit trace: Trace): UIO[FiberRefs] =
    ZIO.withFiberRuntime[Any, Nothing, FiberRefs] { (fiberState, _) =>
      Exit.succeed(fiberState.getFiberRefs())
    }

  /**
   * Lifts an Option into a ZIO, if the option is not defined it fails with
   * NoSuchElementException.
   */
  final def getOrFail[A](v: => Option[A])(implicit trace: Trace): Task[A] =
    getOrFailWith(new NoSuchElementException("None.get"))(v)

  /**
   * Lifts an Option into a IO, if the option is not defined it fails with Unit.
   */
  final def getOrFailUnit[A](v: => Option[A])(implicit trace: Trace): IO[Unit, A] =
    getOrFailWith(())(v)

  /**
   * Lifts an Option into a ZIO. If the option is not defined, fail with the `e`
   * value.
   */
  final def getOrFailWith[E, A](e: => E)(v: => Option[A])(implicit trace: Trace): IO[E, A] =
    suspendSucceed(v match {
      case None    => ZIO.fail(e)
      case Some(v) => Exit.succeed(v)
    })

  /**
   * Gets a state from the environment.
   */
  def getState[S: EnvironmentTag](implicit trace: Trace): ZIO[ZState[S], Nothing, S] =
    ZIO.serviceWithZIO(_.get)

  /**
   * Gets a state from the environment and uses it to run the specified
   * function.
   */
  def getStateWith[S]: ZIO.GetStateWithPartiallyApplied[S] =
    new ZIO.GetStateWithPartiallyApplied[S]

  /**
   * Runs `onTrue` if the result of `b` is `true` and `onFalse` otherwise.
   */
  def ifZIO[R, E](b: => ZIO[R, E, Boolean]): ZIO.IfZIO[R, E] =
    new ZIO.IfZIO(() => b)

  /**
   * Like [[never]], but fibers that running this effect won't be garbage
   * collected unless interrupted.
   */
  def infinity(implicit trace: Trace): UIO[Nothing] =
    ZIO.sleep(Duration.fromNanos(Long.MaxValue)) *> ZIO.never

  /**
   * Inherits values from all [[FiberRef]] instances into current fiber.
   */
  def inheritFiberRefs(childFiberRefs: FiberRefs)(implicit trace: Trace): UIO[Unit] =
    ZIO.updateFiberRefs { (parentFiberId, parentFiberRefs) =>
      parentFiberRefs.joinAs(parentFiberId)(childFiberRefs)
    }

  /**
   * Returns an effect that is interrupted as if by the fiber calling this
   * method.
   */
  def interrupt(implicit trace: Trace): UIO[Nothing] =
    fiberIdWith(interruptAs(_))

  /**
   * Returns an effect that is interrupted as if by the specified fiber.
   */
  def interruptAs(fiberId: => FiberId)(implicit trace: Trace): UIO[Nothing] =
    failCause(Cause.interrupt(fiberId))

  /**
   * Prefix form of `ZIO#interruptible`.
   */
  def interruptible[R, E, A](zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed(zio.interruptible)

  /**
   * Makes the effect interruptible, but passes it a restore function that can
   * be used to restore the inherited interruptibility from whatever region the
   * effect is composed into.
   */
  def interruptibleMask[R, E, A](
    k: ZIO.InterruptibilityRestorer => ZIO[R, E, A]
  )(implicit trace: Trace): ZIO[R, E, A] =
    interruptionMasked(enable = true, k)

  private def interruptionMasked[R, E, A](
    enable: Boolean,
    f: InterruptibilityRestorer => ZIO[R, E, A]
  )(implicit trace: Trace) =
    ZIO.UpdateRuntimeFlagsWithin(
      trace,
      if (enable) RuntimeFlags.enableInterruption else RuntimeFlags.disableInterruption,
      oldFlags =>
        if (RuntimeFlags.interruption(oldFlags)) f(InterruptibilityRestorer.MakeInterruptible)
        else f(InterruptibilityRestorer.MakeUninterruptible)
    )

  /**
   * Retrieves the definition of a fatal error.
   */
  def isFatal(implicit trace: Trace): UIO[Throwable => Boolean] =
    isFatalWith(ZIO.successFn)

  /**
   * Constructs an effect based on the definition of a fatal error.
   */
  def isFatalWith[R, E, A](f: (Throwable => Boolean) => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    FiberRef.currentFatal.getWith(f)

  /**
   * Iterates with the specified effectual function. The moral equivalent of:
   *
   * {{{
   * var s = initial
   *
   * while (cont(s)) {
   *   s = body(s)
   * }
   *
   * s
   * }}}
   */
  def iterate[R, E, S](
    initial: => S
  )(cont: S => Boolean)(body: S => ZIO[R, E, S])(implicit trace: Trace): ZIO[R, E, S] =
    ZIO.suspendSucceed {
      var result = initial

      ZIO
        .whileLoop(cont(result))(body(result))(result = _)
        .as(result)
    }

  /**
   * A low-level while-loop with direct support in the ZIO runtime. The only
   * reason to use this constructor is performance.
   *
   * See [[ZIO.iterate]] for a user-friendly version of this operator that is
   * compatible with purely functional code.
   */
  def whileLoop[R, E, A](check: => Boolean)(body: => ZIO[R, E, A])(process: A => Any)(implicit
    trace: Trace
  ): ZIO[R, E, Unit] =
    ZIO.WhileLoop(trace, () => check, () => body, process)

  /**
   * Returns an effect with the value on the left part.
   */
  def left[A](a: => A)(implicit trace: Trace): UIO[Either[A, Nothing]] =
    succeed(Left(a))

  /**
   * Retrieves the current loggers for this effect.
   */
  def loggers(implicit trace: Trace): UIO[Set[ZLogger[String, Any]]] =
    FiberRef.currentLoggers.get

  /**
   * Constructs an effect based on the current loggers.
   */
  def loggersWith[R, E, A](f: Set[ZLogger[String, Any]] => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    FiberRef.currentLoggers.getWith(f)

  /**
   * Loops with the specified effectual function, collecting the results into a
   * list. The moral equivalent of:
   *
   * {{{
   * var s  = initial
   * var as = List.empty[A]
   *
   * while (cont(s)) {
   *   as = body(s) :: as
   *   s  = inc(s)
   * }
   *
   * as.reverse
   * }}}
   */
  def loop[R, E, A, S](
    initial: => S
  )(cont: S => Boolean, inc: S => S)(body: S => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, List[A]] =
    ZIO.suspendSucceed {

      def loop(initial: S): ZIO[R, E, List[A]] =
        if (cont(initial))
          body(initial).flatMap(a => loop(inc(initial)).map(as => a :: as))
        else
          Exit.succeed(List.empty[A])

      loop(initial)
    }

  /**
   * Loops with the specified effectual function purely for its effects. The
   * moral equivalent of:
   *
   * {{{
   * var s = initial
   *
   * while (cont(s)) {
   *   body(s)
   *   s = inc(s)
   * }
   * }}}
   */
  def loopDiscard[R, E, S](
    initial: => S
  )(cont: S => Boolean, inc: S => S)(body: S => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.suspendSucceed {

      def loop(initial: S): ZIO[R, E, Unit] =
        if (cont(initial)) body(initial) *> loop(inc(initial))
        else Exit.unit

      loop(initial)
    }

  /**
   * Returns a new effect where boolean value of this effect is negated.
   */
  def not[R, E](effect: => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Boolean] =
    ZIO.suspendSucceed(effect.negate)

  /**
   * Logs the specified message at the current log level.
   */
  def log(message: => String)(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, Cause.empty, None, trace)

      Exit.unit
    }

  /**
   * Logs the specified cause at the current log level.
   */
  def logCause(cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    logCause("An error occurred", cause)

  /**
   * Logs the specified message and cause at the current log level.
   */
  def logCause(message: => String, cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, cause, None, trace)

      Exit.unit
    }

  /**
   * Annotates each log in this effect with the specified log annotation.
   */
  def logAnnotate(key: => String, value: => String): LogAnnotate =
    logAnnotate(LogAnnotation(key, value))

  /**
   * Annotates each log in this effect with the specified log annotation.
   */
  def logAnnotate(logAnnotation: => LogAnnotation, logAnnotations: LogAnnotation*): LogAnnotate =
    logAnnotate(Set(logAnnotation) ++ logAnnotations.toSet)

  /**
   * Annotates each log in this effect with the specified log annotation.
   */
  def logAnnotate(logAnnotations: => Set[LogAnnotation]): LogAnnotate =
    new LogAnnotate(() => logAnnotations)

  def logAnnotateScoped(key: => String, value: => String)(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    logAnnotateScoped(LogAnnotation(key, value))

  def logAnnotateScoped(logAnnotation: => LogAnnotation, logAnnotations: LogAnnotation*)(implicit
    trace: Trace
  ): ZIO[Scope, Nothing, Unit] =
    logAnnotateScoped(Set(logAnnotation) ++ logAnnotations.toSet)

  def logAnnotateScoped(logAnnotations: => Set[LogAnnotation])(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    FiberRef.currentLogAnnotations.locallyScopedWith(_ ++ logAnnotations.map { case LogAnnotation(key, value) =>
      key -> value
    })

  /**
   * Retrieves the log annotations associated with the current scope.
   */
  def logAnnotations(implicit trace: Trace): UIO[Map[String, String]] =
    FiberRef.currentLogAnnotations.get

  /**
   * Logs the specified message at the debug log level.
   */
  def logDebug(message: => String)(implicit trace: Trace): UIO[Unit] =
    logDebugCause(message, Cause.empty)

  /**
   * Logs the specified cause at the debug log level.
   */
  def logDebugCause(message: => String, cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, cause, someDebug, trace)

      Exit.unit
    }

  /**
   * Logs the specified cause at the debug log level..
   */
  def logDebugCause(cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    logDebugCause("", cause)

  /**
   * Logs the specified message at the error log level.
   */
  def logError(message: => String)(implicit trace: Trace): UIO[Unit] =
    logErrorCause(message, Cause.empty)

  /**
   * Logs the specified cause as an error.
   */
  def logErrorCause(message: => String, cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, cause, someError, trace)

      Exit.unit
    }

  /**
   * Logs the specified cause as an error.
   */
  def logErrorCause(cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    logErrorCause("", cause)

  /**
   * Logs the specified message at the fatal log level.
   */
  def logFatal(message: => String)(implicit trace: Trace): UIO[Unit] =
    logFatalCause(message, Cause.empty)

  /**
   * Logs the specified cause at the fatal log level.
   */
  def logFatalCause(message: => String, cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, cause, someFatal, trace)

      Exit.unit
    }

  /**
   * Logs the specified cause at the fatal log level.
   */
  def logFatalCause(cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    logFatalCause("", cause)

  /**
   * Logs the specified message at the informational log level.
   */
  def logInfo(message: => String)(implicit trace: Trace): UIO[Unit] =
    logInfoCause(message, Cause.empty)

  /**
   * Logs the specified cause at the informational log level.
   */
  def logInfoCause(message: => String, cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, cause, someInfo, trace)

      Exit.unit
    }

  /**
   * Logs the specified cause at the informational log level..
   */
  def logInfoCause(cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    logInfoCause("", cause)

  /**
   * Sets the log level for this effect.
   * {{{
   * ZIO.logLevel(LogLevel.Warning) {
   *   ZIO.log("The response time exceeded its threshold!")
   * }
   * }}}
   */
  def logLevel(level: LogLevel): LogLevel =
    level

  def logLevelScoped(level: LogLevel)(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    FiberRef.currentLogLevel.locallyScoped(level)

  /**
   * Adjusts the label for the current logging span.
   * {{{
   * ZIO.logSpan("parsing") { parseRequest(req) }
   * }}}
   */
  def logSpan(label: => String): LogSpan = new LogSpan(() => label)

  def logSpanScoped(label: => String)(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    FiberRef.currentLogSpan.getWith { stack =>
      val instant = java.lang.System.currentTimeMillis()
      val logSpan = zio.LogSpan(label, instant)

      FiberRef.currentLogSpan.locallyScoped(logSpan :: stack)
    }

  /**
   * Retrieves the log spans associated with the current scope.
   */
  def logSpans(implicit trace: Trace): UIO[List[zio.LogSpan]] =
    FiberRef.currentLogSpan.get

  /**
   * Logs the specified message at the trace log level.
   */
  def logTrace(message: => String)(implicit trace: Trace): UIO[Unit] =
    logTraceCause(message, Cause.empty)

  /**
   * Logs the specified cause at the trace log level.
   */
  def logTraceCause(message: => String, cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, cause, someTrace, trace)

      Exit.unit
    }

  /**
   * Logs the specified cause at the trace log level..
   */
  def logTraceCause(cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    logTraceCause("", cause)

  /**
   * Logs the specified message at the warning log level.
   */
  def logWarning(message: => String)(implicit trace: Trace): UIO[Unit] =
    logWarningCause(message, Cause.empty)

  /**
   * Logs the specified cause at the warning log level.
   */
  def logWarningCause(message: => String, cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberState, _) =>
      fiberState.log(() => message, cause, someWarning, trace)

      Exit.unit
    }

  /**
   * Logs the specified cause at the warning log level..
   */
  def logWarningCause(cause: => Cause[Any])(implicit trace: Trace): UIO[Unit] =
    logWarningCause("", cause)

  /**
   * Returns a memoized version of the specified effectual function.
   */
  def memoize[R, E, A, B](f: A => ZIO[R, E, B])(implicit trace: Trace): UIO[A => ZIO[R, E, B]] =
    Ref.Synchronized.make(Map.empty[A, Promise[E, (FiberRefs.Patch, B)]]).map { ref => a =>
      for {
        promise <- ref.modifyZIO { map =>
                     map.get(a) match {
                       case Some(promise) => Exit.succeed((promise, map))
                       case None =>
                         for {
                           promise <- Promise.make[E, (FiberRefs.Patch, B)]
                           _       <- f(a).diffFiberRefs.intoPromise(promise).fork
                         } yield (promise, map.updated(a, promise))
                     }
                   }
        b <- promise.await.flatMap { case (patch, b) => ZIO.patchFiberRefs(patch).as(b) }
      } yield b
    }

  /**
   * Merges an `Iterable[IO]` to a single IO, working sequentially.
   */
  def mergeAll[R, E, A, B](
    in: => Iterable[ZIO[R, E, A]]
  )(zero: => B)(f: (B, A) => B)(implicit trace: Trace): ZIO[R, E, B] =
    ZIO.foldLeft(in)(zero)((b, zio) => zio.map(a => f(b, a)))

  /**
   * Merges an `Iterable[IO]` to a single IO, working in parallel.
   *
   * Due to the parallel nature of this combinator, `f` must be both:
   *   - commutative: `f(a, b) == f(b, a)`
   *   - associative: `f(a, f(b, c)) == f(f(a, b), c)`
   *
   * It's unsafe to execute side effects inside `f`, as `f` may be executed more
   * than once for some of `in` elements during effect execution.
   */
  def mergeAllPar[R, E, A, B](
    in: => Iterable[ZIO[R, E, A]]
  )(zero: => B)(f: (B, A) => B)(implicit trace: Trace): ZIO[R, E, B] =
    Ref.make(zero).flatMap(acc => foreachParDiscard(in)(_.flatMap(a => acc.update(f(_, a)))) *> acc.get)

  /**
   * Gets current metrics snapshot.
   */
  def metrics(implicit trace: Trace): UIO[Metrics] =
    ZIO.succeedUnsafe { implicit u =>
      Metrics(internal.metrics.metricRegistry.snapshot())
    }

  /**
   * Returns a effect that will never produce anything. The moral equivalent of
   * `while(true) {}`, only without the wasted CPU cycles. Fibers that execute
   * this effect will be automatically garbage collected on the JVM when no
   * explicit references to them are held, because they cannot be reactivated.
   */
  def never(implicit trace: Trace): UIO[Nothing] =
    async[Any, Nothing, Nothing](_ => ())

  /**
   * Returns an effect that succeeds with the `None` value.
   */
  val none: UIO[Option[Nothing]] =
    succeed(None)(Trace.empty)

  /**
   * Lifts an Option into a ZIO. If the option is empty it succeeds with Unit.
   * If the option is defined it fails with the content.
   */
  def noneOrFail[E](o: => Option[E])(implicit trace: Trace): IO[E, Unit] =
    getOrFailUnit(o).flip

  /**
   * Lifts an Option into a ZIO. If the option is empty it succeeds with Unit.
   * If the option is defined it fails with an error adapted with f.
   */
  def noneOrFailWith[E, O](o: => Option[O])(f: O => E)(implicit trace: Trace): IO[E, Unit] =
    getOrFailUnit(o).flip.mapError(f)

  /**
   * Returns an effect that will execute the specified effect fully on the
   * provided executor, before potentially returning to the previous executor.
   * See [[ZIO!.onExecutor]].
   */
  def onExecutor[R, E, A](executor: => Executor)(zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.descriptorWith { descriptor =>
      val oldExecutor = descriptor.executor
      val newExecutor = executor

      if (descriptor.isLocked && oldExecutor == newExecutor)
        zio
      else if (descriptor.isLocked)
        ZIO.acquireReleaseWith(ZIO.shift(newExecutor))(_ => ZIO.shift(oldExecutor))(_ => zio)
      else
        ZIO.acquireReleaseWith(ZIO.shift(newExecutor))(_ => ZIO.unshift)(_ => zio)
    }

  /**
   * Shifts execution to the specified executor and shifts it back to the
   * previous executor, if any, when the scope is closed.
   */
  def onExecutorScoped(executor: => Executor)(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    ZIO.descriptorWith { descriptor =>
      val oldExecutor = descriptor.executor
      val newExecutor = executor

      if (descriptor.isLocked && oldExecutor == newExecutor)
        Exit.unit
      else if (descriptor.isLocked)
        ZIO.acquireRelease(ZIO.shift(executor))(_ => ZIO.shift(descriptor.executor))
      else
        ZIO.acquireRelease(ZIO.shift(executor))(_ => ZIO.unshift)
    }

  /**
   * Applies the specified changes to the `FiberRef` values for the fiber
   * running this workflow.
   */
  def patchFiberRefs(patch: FiberRefs.Patch)(implicit trace: Trace): ZIO[Any, Nothing, Unit] =
    updateFiberRefs(patch)

  /**
   * Returns a new scoped workflow that runs finalizers added to the scope of
   * this workflow in parallel.
   */
  def parallelFinalizers[R, E, A](zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.environmentWithZIO[R] { environment =>
      environment.getDynamic[Scope] match {
        case None => zio
        case Some(scope) =>
          scope.executionStrategy match {
            case ExecutionStrategy.Parallel => zio
            case _                          => scope.forkWith(ExecutionStrategy.Parallel).flatMap(_.extend[R](zio))
          }
      }
    }

  def parallelFinalizersMask[R, E, A](f: ZIO.FinalizersRestorer => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.environmentWithZIO[R] { environment =>
      environment.getDynamic[Scope] match {
        case None => f(ZIO.FinalizersRestorer.Identity)
        case Some(scope) =>
          scope.executionStrategy match {
            case ExecutionStrategy.Parallel     => ZIO.parallelFinalizers(f(ZIO.FinalizersRestorer.MakeParallel))
            case ExecutionStrategy.ParallelN(n) => ZIO.parallelFinalizers(f(ZIO.FinalizersRestorer.MakeParallelN(n)))
            case ExecutionStrategy.Sequential   => ZIO.parallelFinalizers(f(ZIO.FinalizersRestorer.MakeSequential))
          }
      }
    }

  /**
   * Retrieves the maximum number of fibers for parallel operators or `None` if
   * it is unbounded.
   */
  def parallelism(implicit trace: Trace): UIO[Option[Int]] =
    Parallelism.get

  /**
   * Retrieves the current maximum number of fibers for parallel operators and
   * uses it to run the specified effect.
   */
  def parallelismWith[R, E, A](f: Option[Int] => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    Parallelism.getWith(f)

  /**
   * Feeds elements of type `A` to a function `f` that returns an effect.
   * Collects all successes and failures in a tupled fashion.
   */
  def partition[R, E, A, B](
    in: => Iterable[A]
  )(f: A => ZIO[R, E, B])(implicit ev: CanFail[E], trace: Trace): ZIO[R, Nothing, (Iterable[E], Iterable[B])] =
    ZIO.suspendSucceed(ZIO.foreach(in)(f(_).either).map(partitionMap(_)(ZIO.identityFn)))

  /**
   * Feeds elements of type `A` to a function `f` that returns an effect.
   * Collects all successes and failures in parallel and returns the result as a
   * tuple.
   */
  def partitionPar[R, E, A, B](
    in: => Iterable[A]
  )(f: A => ZIO[R, E, B])(implicit ev: CanFail[E], trace: Trace): ZIO[R, Nothing, (Iterable[E], Iterable[B])] =
    ZIO.suspendSucceed(ZIO.foreachPar(in)(f(_).either).map(ZIO.partitionMap(_)(ZIO.identityFn)))

  /**
   * Given an environment `R`, returns a function that can supply the
   * environment to programs that require it, removing their need for any
   * specific environment.
   *
   * This is similar to dependency injection, and the `provide` function can be
   * thought of as `inject`.
   */
  def provideEnvironment[R, E, A](r: => ZEnvironment[R])(implicit trace: Trace): ZIO[R, E, A] => IO[E, A] =
    _.provideEnvironment(r)

  def provideLayer[RIn, E, ROut, RIn2, ROut2](layer: ZLayer[RIn, E, ROut])(
    zio: ZIO[ROut with RIn2, E, ROut2]
  )(implicit ev: EnvironmentTag[RIn2], tag: EnvironmentTag[ROut], trace: Trace): ZIO[RIn with RIn2, E, ROut2] =
    zio.provideSomeLayer[RIn with RIn2](ZLayer.environment[RIn2] ++ layer)

  /**
   * Races an `IO[E, A]` against zero or more other effects. Yields either the
   * first success or the last failure.
   */
  def raceAll[R, R1 <: R, E, A](
    zio: => ZIO[R, E, A],
    ios: => Iterable[ZIO[R1, E, A]]
  )(implicit trace: Trace): ZIO[R1, E, A] =
    ZIO.suspendSucceed(zio.raceAll(ios))

  /**
   * Returns an effect that races this effect with all the specified effects,
   * yielding the first result to complete, whether by success or failure. If
   * neither effect completes, then the composed effect will not complete.
   *
   * WARNING: The raced effect will safely interrupt the "losers", but will not
   * resume until the losers have been cleanly terminated. If early return is
   * desired, then instead of performing `ZIO.raceFirst(l, rs)`, perform
   * `ZIO.raceFirst(l.disconnect, rs.map(_.disconnect))`, which disconnects left
   * and rights interrupt signal, allowing a fast return, with interruption
   * performed in the background.
   */
  def raceFirst[R, R1 <: R, E, A](zio: ZIO[R, E, A], ios: Iterable[ZIO[R1, E, A]])(implicit
    trace: Trace
  ): ZIO[R1, E, A] =
    (zio.exit raceAll ios.map(_.exit)).unexit

  /**
   * Retreives the `Random` service for this workflow.
   */
  def random(implicit trace: Trace): UIO[Random] =
    ZIO.randomWith(ZIO.successFn)

  /**
   * Retrieves the `Random` service for this workflow and uses it to run the
   * specified workflow.
   */
  def randomWith[R, E, A](f: Random => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    DefaultServices.currentServices.getWith(services => f(services.get(Random.tag)))

  /**
   * Reduces an `Iterable[IO]` to a single `IO`, working sequentially.
   */
  def reduceAll[R, R1 <: R, E, A](a: => ZIO[R, E, A], as: => Iterable[ZIO[R1, E, A]])(
    f: (A, A) => A
  )(implicit trace: Trace): ZIO[R1, E, A] =
    ZIO.suspendSucceed(a.flatMap(ZIO.mergeAll(as)(_)(f)))

  /**
   * Reduces an `Iterable[IO]` to a single `IO`, working in parallel.
   */
  def reduceAllPar[R, R1 <: R, E, A](a0: => ZIO[R, E, A], as0: => Iterable[ZIO[R1, E, A]])(
    f: (A, A) => A
  )(implicit trace: Trace): ZIO[R1, E, A] =
    ZIO.suspendSucceed {
      val a  = a0
      val as = as0

      def prepend[Z](z: Z, zs: Iterable[Z]): Iterable[Z] =
        new Iterable[Z] {
          override def iterator: Iterator[Z] = Iterator(z) ++ zs.iterator
        }

      val all = prepend(a, as)
      mergeAllPar(all)(Option.empty[A])((acc, elem) => Some(acc.fold(elem)(f(_, elem)))).map(_.get)
    }

  // NOTE: Prefer using `Exit.failCause` which doesn't require the unused implicit `trace`
  def refailCause[E](cause: Cause[E])(implicit trace: Trace): ZIO[Any, E, Nothing] = Exit.Failure(cause)

  /**
   * Replicates the given effect `n` times. If 0 or negative numbers are given,
   * an empty `Iterable` will be returned. This method is more efficient than
   * using `List.fill` or similar methods, because the returned `Iterable`
   * consumes only a small amount of heap regardless of `n`.
   */
  def replicate[R, E, A](n: Int)(effect: ZIO[R, E, A])(implicit trace: Trace): Iterable[ZIO[R, E, A]] =
    new Iterable[ZIO[R, E, A]] {
      override def iterator: Iterator[ZIO[R, E, A]] = Iterator.range(0, n).map(_ => effect)
    }

  /**
   * Performs this effect the specified number of times and collects the
   * results.
   */
  def replicateZIO[R, E, A](n: => Int)(effect: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, Iterable[A]] =
    ZIO.suspendSucceed(ZIO.collectAll(ZIO.replicate(n)(effect)))

  /**
   * Performs this effect the specified number of times, discarding the results.
   */
  def replicateZIODiscard[R, E, A](n: => Int)(effect: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.collectAllDiscard(ZIO.replicate(n)(effect))

  /**
   * Returns an effect with the value on the right part.
   */
  def right[B](b: => B)(implicit trace: Trace): UIO[Either[Nothing, B]] =
    succeed(Right(b))

  /**
   * Returns an effect that accesses the runtime, which can be used to
   * (unsafely) execute tasks. This is useful for integration with legacy code
   * that must call back into ZIO code.
   */
  def runtime[R](implicit trace: Trace): URIO[R, Runtime[R]] =
    ZIO.withFiberRuntime[R, Nothing, Runtime[R]] { (state, status) =>
      val fiberRefs    = state.getFiberRefs()
      val environment  = fiberRefs.getOrDefault(FiberRef.currentEnvironment).asInstanceOf[ZEnvironment[R]]
      val runtimeFlags = status.runtimeFlags
      Exit.succeed(Runtime(environment, fiberRefs, runtimeFlags))
    }

  /**
   * Retrieves an effect that succeeds with the current runtime flags, which
   * govern behavior and features of the runtime system.
   */
  def runtimeFlags(implicit trace: Trace): ZIO[Any, Nothing, RuntimeFlags] =
    ZIO.withFiberRuntime[Any, Nothing, RuntimeFlags] { (_, status) =>
      Exit.succeed(status.runtimeFlags)
    }

  /**
   * Returns the current scope.
   */
  def scope(implicit trace: Trace): ZIO[Scope, Nothing, Scope] =
    ZIO.service[Scope]

  /**
   * Scopes all resources used in this effect to the lifetime of the effect,
   * ensuring that their finalizers are run as soon as this effect completes
   * execution, whether by success, failure, or interruption.
   *
   * {{{
   * ZIO.scoped {
   *   openFile(name).flatMap(useFile)
   * }
   * }}}
   */
  def scoped[R]: ScopedPartiallyApplied[R] =
    new ScopedPartiallyApplied[R]

  /**
   * Creates a scope, uses it to perform the specified effect, and closes the
   * scope as soon as the effect completes, whether by success, failure, or
   * interruption.
   */
  def scopedWith[R, E, A](f: Scope => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    Scope.make.flatMap(scope => f(scope).onExit(scope.close(_)))

  /**
   * Accesses the current scope and uses it to perform the specified effect.
   */
  def scopeWith[R, E, A](f: Scope => ZIO[R, E, A])(implicit trace: Trace): ZIO[R with Scope, E, A] =
    ZIO.serviceWithZIO[Scope](f)

  /**
   * Returns a new scoped workflow that runs finalizers added to the scope of
   * this workflow sequentially in the reverse of the order in which they were
   * added. Note that finalizers are run sequentially by default so this only
   * has meaning if used within a scope where finalizers are being run in
   * parallel.
   */
  def sequentialFinalizers[R, E, A](zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.environmentWithZIO[R] { environment =>
      environment.getDynamic[Scope] match {
        case None => zio
        case Some(scope) =>
          scope.executionStrategy match {
            case ExecutionStrategy.Sequential => zio
            case _                            => scope.forkWith(ExecutionStrategy.Sequential).flatMap(_.extend[R](zio))
          }
      }
    }

  /**
   * Sets the `FiberRef` values for the fiber running this effect to the values
   * in the specified collection of `FiberRef` values.
   */
  def setFiberRefs(fiberRefs: => FiberRefs)(implicit trace: Trace): UIO[Unit] =
    ZIO.suspendSucceed(fiberRefs.setAll)

  /**
   * Sets a state in the environment to the specified value.
   */
  def setState[S: EnvironmentTag](s: => S)(implicit trace: Trace): ZIO[ZState[S], Nothing, Unit] =
    ZIO.serviceWithZIO(_.set(s))

  /**
   * Accesses the specified service in the environment of the effect.
   */
  def service[A: Tag](implicit trace: Trace): URIO[A, A] =
    serviceWith(identity)

  /**
   * Accesses the service corresponding to the specified key in the environment.
   */
  def serviceAt[Service]: ServiceAtPartiallyApplied[Service] =
    new ServiceAtPartiallyApplied[Service]

  /**
   * Accesses the specified service in the environment of the effect.
   *
   * Especially useful for creating "accessor" methods on Services' companion
   * objects.
   *
   * {{{
   * def foo(int: Int) = ZIO.serviceWith[Foo](_.foo(int))
   * }}}
   */
  def serviceWith[Service]: ServiceWithPartiallyApplied[Service] =
    new ServiceWithPartiallyApplied[Service]

  /**
   * Effectfully accesses the specified service in the environment of the
   * effect.
   *
   * Especially useful for creating "accessor" methods on Services' companion
   * objects.
   *
   * {{{
   * def foo(int: Int) = ZIO.serviceWithZIO[Foo](_.foo(int))
   * }}}
   */
  def serviceWithZIO[Service]: ServiceWithZIOPartiallyApplied[Service] =
    new ServiceWithZIOPartiallyApplied[Service]

  /**
   * Returns an effect that shifts execution to the specified executor. This is
   * useful to specify a default executor that effects sequenced after this one
   * will be run on if they are not shifted somewhere else. It can also be used
   * to implement higher level operators to manage where an effect is run such
   * as [[ZIO!.onExecutor]] and [[ZIO!.onExecutionContext]].
   */
  def shift(executor: => Executor)(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (fiberRuntime, _) =>
      val newExecutor = executor
      fiberRuntime.getFiberRef(FiberRef.overrideExecutor) match {
        case None =>
          fiberRuntime.setFiberRef(FiberRef.overrideExecutor, Some(newExecutor))
          fiberRuntime.getRunningExecutor() match {
            case Some(runningExecutor) if runningExecutor == newExecutor => Exit.unit
            case _                                                       => ZIO.yieldNow
          }
        case Some(overrideExecutor) =>
          if (overrideExecutor eq newExecutor) Exit.unit
          else {
            fiberRuntime.setFiberRef(FiberRef.overrideExecutor, Some(newExecutor))
            ZIO.yieldNow
          }
      }
    }

  /**
   * Returns an effect that suspends for the specified duration. This method is
   * asynchronous, and does not actually block the fiber executing the effect.
   */
  def sleep(duration: => Duration)(implicit trace: Trace): UIO[Unit] =
    Clock.sleep(duration)

  /**
   * Returns an effect with the optional value.
   */
  def some[A](a: => A)(implicit trace: Trace): UIO[Option[A]] =
    succeed(Some(a))

  /**
   * Provides a stateful ZIO workflow with its initial state, resulting in a
   * workflow that is ready to be run.
   *
   * {{{
   * ZIO.stateful(0) {
   *   for {
   *       _     <- ZIO.updateState[Int](_ + 1)
   *       state <- ZIO.getState[Int]
   *     } yield assertTrue(state == 1)
   *   }
   * }}}
   */
  def stateful[R]: StatefulPartiallyApplied[R] =
    new StatefulPartiallyApplied[R]

  /**
   * Provides a stateful ZIO workflow with its initial state, using the
   * specified patch type to combine updates to the state by different fibers in
   * a compositional way.
   */
  def statefulPatch[R]: StatefulPatchPartiallyApplied[R] =
    new StatefulPatchPartiallyApplied[R]

  def succeedBlockingUnsafe[A](a: Unsafe => A)(implicit trace: Trace): UIO[A] =
    ZIO.blocking(ZIO.succeedUnsafe(a))

  def succeedUnsafe[A](a: Unsafe => A)(implicit trace: Trace): UIO[A] =
    ZIO.succeed(a(Unsafe))

  /**
   * Returns a lazily constructed effect, whose construction may itself require
   * effects. When no environment is required (i.e., when R == Any) it is
   * conceptually equivalent to `flatten(effect(io))`.
   */
  def suspend[R, A](rio: => RIO[R, A])(implicit trace: Trace): RIO[R, A] =
    ZIO.suspendSucceed {
      try rio
      catch {
        case t: Throwable =>
          ZIO.isFatalWith { isFatal =>
            if (!isFatal(t)) Exit.Failure(Cause.fail(t))
            else throw t
          }
      }
    }

  /**
   * Returns a lazily constructed effect, whose construction may itself require
   * effects. The effect must not throw any exceptions. When no environment is
   * required (i.e., when R == Any) it is conceptually equivalent to
   * `flatten(succeed(zio))`. If you wonder if the effect throws exceptions, do
   * not use this method, use [[ZIO.suspend]].
   */
  def suspendSucceed[R, E, A](zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.succeed(zio).flatMap(identityFn)

  def suspendSucceedUnsafe[R, E, A](zio: Unsafe => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.succeedUnsafe(zio).flatMap(identityFn)

  /**
   * Retrieves the `System` service for this workflow.
   */
  def system(implicit trace: Trace): UIO[System] =
    ZIO.systemWith(ZIO.successFn)

  /**
   * Retrieves the `System` service for this workflow and uses it to run the
   * specified workflow.
   */
  def systemWith[R, E, A](f: System => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    DefaultServices.currentServices.getWith(services => f(services.get(System.tag)))

  /**
   * Capture ZIO stack trace at the current point.
   */
  def stackTrace(implicit trace: Trace): UIO[StackTrace] =
    GenerateStackTrace(trace)

  /**
   * Tags each metric in this effect with the specific tag.
   */
  def tagged(key: => String, value: => String): Tagged =
    tagged(Set(MetricLabel(key, value)))

  /**
   * Tags each metric in this effect with the specific tag.
   */
  def tagged(tag: => MetricLabel, tags: MetricLabel*): Tagged =
    tagged(Set(tag) ++ tags.toSet)

  /**
   * Tags each metric in this effect with the specific tag.
   */
  def tagged(tags: => Set[MetricLabel]): Tagged =
    new Tagged(() => tags)

  def taggedScoped(key: => String, value: => String)(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    taggedScoped(Set(MetricLabel(key, value)))

  def taggedScoped(tag: => MetricLabel, tags: MetricLabel*)(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    taggedScoped(Set(tag) ++ tags.toSet)

  def taggedScoped(tags: => Set[MetricLabel])(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    FiberRef.currentTags.locallyScopedWith(_ ++ tags)

  /**
   * Retrieves the metric tags associated with the current scope.
   */
  def tags(implicit trace: Trace): ZIO[Any, Nothing, Set[MetricLabel]] =
    FiberRef.currentTags.get

  /**
   * Transplants specified effects so that when those effects fork other
   * effects, the forked effects will be governed by the scope of the fiber that
   * executes this effect.
   *
   * This can be used to "graft" deep grandchildren onto a higher-level scope,
   * effectively extending their lifespans into the parent scope.
   */
  def transplant[R, E, A](f: Grafter => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.withFiberRuntime[R, E, A] { (fiberState, _) =>
      val scopeOverride = fiberState.getFiberRef(FiberRef.forkScopeOverride)
      val scope         = scopeOverride.getOrElse(fiberState.scope)

      f(new Grafter(scope))
    }

  /**
   * An effect that succeeds with a unit value.
   */
  val unit: UIO[Unit] =
    succeed(())(Trace.empty)

  /**
   * Prefix form of `ZIO#uninterruptible`.
   */
  def uninterruptible[R, E, A](zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed(zio).uninterruptible

  /**
   * Makes the effect uninterruptible, but passes it a restore function that can
   * be used to restore the inherited interruptibility from whatever region the
   * effect is composed into.
   */
  def uninterruptibleMask[R, E, A](
    f: ZIO.InterruptibilityRestorer => ZIO[R, E, A]
  )(implicit trace: Trace): ZIO[R, E, A] =
    interruptionMasked(enable = false, f)

  /**
   * The moral equivalent of `if (!p) Some(exp) else None`
   */
  def unless[R, E, A](p: => Boolean)(zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, Option[A]] =
    suspendSucceed(if (p) Exit.none else zio.asSome)

  /**
   * The moral equivalent of `if (!p) { expr; () }`
   */
  def unlessDiscard[R, E](p: => Boolean)(zio: => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
    suspendSucceed(if (p) Exit.unit else zio.unit)

  /**
   * The moral equivalent of `if (!p) Some(expr) else None` when `p` has
   * side-effects
   */
  def unlessZIO[R, E](p: => ZIO[R, E, Boolean]): ZIO.UnlessZIO[R, E] =
    new ZIO.UnlessZIO(() => p)

  /**
   * The moral equivalent of `if (!p) { expr; () }` when `p` has side-effects
   */
  def unlessZIODiscard[R, E](p: => ZIO[R, E, Boolean]): ZIO.UnlessZIODiscard[R, E] =
    new ZIO.UnlessZIODiscard(() => p)

  /**
   * The inverse operation `IO.sandboxed`
   *
   * Terminates with exceptions on the `Left` side of the `Either` error, if it
   * exists. Otherwise extracts the contained `IO[E, A]`
   */
  def unsandbox[R, E, A](v: => ZIO[R, Cause[E], A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed(v.mapErrorCause(_.flatten))

  /**
   * Returns an effect indicating that execution is no longer required to be
   * performed on the current executor. The runtime may continue executing on
   * this executor for efficiency but will not automatically shift back to it
   * after completing an effect on another executor.
   */
  def unshift(implicit trace: Trace): UIO[Unit] =
    FiberRef.overrideExecutor.set(None)

  /**
   * Updates the `FiberRef` values for the fiber running this effect using the
   * specified function.
   */
  def updateFiberRefs(f: (FiberId.Runtime, FiberRefs) => FiberRefs)(implicit trace: Trace): UIO[Unit] =
    ZIO.withFiberRuntime[Any, Nothing, Unit] { (state, _) =>
      state.setFiberRefs(f(state.id, state.getFiberRefs()))

      Exit.unit
    }

  /**
   * Updates the runtime flags. This may have a performance impact. For a
   * higher-performance variant, see `ZIO#withRuntimeFlags`.
   */
  def updateRuntimeFlags(patch: RuntimeFlags.Patch)(implicit trace: Trace): ZIO[Any, Nothing, Unit] =
    if (patch == RuntimeFlags.Patch.empty) Exit.unit
    else ZIO.UpdateRuntimeFlags(trace, patch)

  /**
   * Updates a state in the environment with the specified function.
   */
  def updateState[S: EnvironmentTag](f: S => S)(implicit trace: Trace): ZIO[ZState[S], Nothing, Unit] =
    ZIO.serviceWithZIO(_.update(f))

  /**
   * Scopes all resources acquired by `resource` to the lifetime of `use`
   * without effecting the scope of any resources acquired by `use`.
   */
  def using[R]: UsingPartiallyApplied[R] =
    new ZIO.UsingPartiallyApplied[R]

  /**
   * Feeds elements of type `A` to `f` and accumulates all errors in error
   * channel or successes in success channel.
   *
   * This combinator is lossy meaning that if there are errors all successes
   * will be lost. To retain all information please use [[partition]].
   */
  def validate[R, E, A, B, Collection[+Element] <: Iterable[Element]](in: Collection[A])(
    f: A => ZIO[R, E, B]
  )(implicit
    bf: BuildFrom[Collection[A], B, Collection[B]],
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R, ::[E], Collection[B]] =
    partition(in)(f).flatMap { case (es, bs) =>
      if (es.isEmpty) Exit.succeed(bf.fromSpecific(in)(bs))
      else ZIO.fail(::(es.head, es.tail.toList))
    }

  /**
   * Feeds elements of type `A` to `f` and accumulates all errors in error
   * channel or successes in success channel.
   *
   * This combinator is lossy meaning that if there are errors all successes
   * will be lost. To retain all information please use [[partition]].
   */
  def validate[R, E, A, B](in: NonEmptyChunk[A])(
    f: A => ZIO[R, E, B]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R, ::[E], NonEmptyChunk[B]] =
    partition(in)(f).flatMap { case (es, bs) =>
      if (es.isEmpty) Exit.succeed(NonEmptyChunk.nonEmpty(Chunk.fromIterable(bs)))
      else ZIO.fail(::(es.head, es.tail.toList))
    }

  /**
   * Feeds elements of type `A` to `f` and accumulates all errors, discarding
   * the successes.
   */
  def validateDiscard[R, E, A](in: => Iterable[A])(f: A => ZIO[R, E, Any])(implicit
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R, ::[E], Unit] =
    partition(in)(f).flatMap { case (es, _) =>
      if (es.isEmpty) Exit.unit
      else ZIO.fail(::(es.head, es.tail.toList))
    }

  /**
   * Feeds elements of type `A` to `f `and accumulates, in parallel, all errors
   * in error channel or successes in success channel.
   *
   * This combinator is lossy meaning that if there are errors all successes
   * will be lost. To retain all information please use [[partitionPar]].
   */
  def validatePar[R, E, A, B, Collection[+Element] <: Iterable[Element]](in: Collection[A])(
    f: A => ZIO[R, E, B]
  )(implicit
    bf: BuildFrom[Collection[A], B, Collection[B]],
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R, ::[E], Collection[B]] =
    partitionPar(in)(f).flatMap { case (es, bs) =>
      if (es.isEmpty) Exit.succeed(bf.fromSpecific(in)(bs))
      else ZIO.fail(::(es.head, es.tail.toList))
    }

  /**
   * Feeds elements of type `A` to `f `and accumulates, in parallel, all errors
   * in error channel or successes in success channel.
   *
   * This combinator is lossy meaning that if there are errors all successes
   * will be lost. To retain all information please use [[partitionPar]].
   */
  def validatePar[R, E, A, B](in: NonEmptyChunk[A])(
    f: A => ZIO[R, E, B]
  )(implicit ev: CanFail[E], trace: Trace): ZIO[R, ::[E], NonEmptyChunk[B]] =
    partitionPar(in)(f).flatMap { case (es, bs) =>
      if (es.isEmpty) Exit.succeed(NonEmptyChunk.nonEmpty(Chunk.fromIterable(bs)))
      else ZIO.fail(::(es.head, es.tail.toList))
    }

  /**
   * Feeds elements of type `A` to `f` in parallel and accumulates all errors,
   * discarding the successes.
   */
  def validateParDiscard[R, E, A](in: => Iterable[A])(f: A => ZIO[R, E, Any])(implicit
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R, ::[E], Unit] =
    partitionPar(in)(f).flatMap { case (es, _) =>
      if (es.isEmpty) Exit.unit
      else ZIO.fail(::(es.head, es.tail.toList))
    }

  /**
   * Feeds elements of type `A` to `f` until it succeeds. Returns first success
   * or the accumulation of all errors.
   */
  def validateFirst[R, E, A, B, Collection[+Element] <: Iterable[Element]](in: Collection[A])(
    f: A => ZIO[R, E, B]
  )(implicit
    bf: BuildFrom[Collection[A], E, Collection[E]],
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R, Collection[E], B] =
    ZIO.foreach(in)(f(_).flip).flip

  /**
   * Feeds elements of type `A` to `f`, in parallel, until it succeeds. Returns
   * first success or the accumulation of all errors.
   *
   * In case of success all other running fibers are terminated.
   */
  def validateFirstPar[R, E, A, B, Collection[+Element] <: Iterable[Element]](in: Collection[A])(
    f: A => ZIO[R, E, B]
  )(implicit
    bf: BuildFrom[Collection[A], E, Collection[E]],
    ev: CanFail[E],
    trace: Trace
  ): ZIO[R, Collection[E], B] =
    ZIO.foreachPar(in)(f(_).flip).flip

  /**
   * The moral equivalent of `if (p) Some(exp) else None`
   */
  def when[R, E, A](p: => Boolean)(zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, Option[A]] =
    suspendSucceed(if (p) zio.asSome else Exit.none)

  /**
   * The moral equivalent of `if (p) { expr; () }`
   */
  def whenDiscard[R, E](p: => Boolean)(zio: => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
    suspendSucceed(if (p) zio.unit else Exit.unit)

  /**
   * Runs an effect when the supplied `PartialFunction` matches for the given
   * value, otherwise does nothing.
   */
  def whenCase[R, E, A, B](a: => A)(pf: PartialFunction[A, ZIO[R, E, B]])(implicit
    trace: Trace
  ): ZIO[R, E, Option[B]] =
    suspendSucceed(pf.andThen(_.asSome).applyOrElse(a, (_: A) => Exit.none))

  /**
   * Runs an effect when the supplied `PartialFunction` matches for the given
   * value, and discards the result, otherwise does nothing.
   */
  def whenCaseDiscard[R, E, A](a: => A)(pf: PartialFunction[A, ZIO[R, E, Any]])(implicit
    trace: Trace
  ): ZIO[R, E, Unit] =
    suspendSucceed(pf.andThen(_.unit).applyOrElse(a, (_: A) => Exit.unit))

  /**
   * Runs an effect when the supplied `PartialFunction` matches for the given
   * effectful value, otherwise does nothing.
   */
  def whenCaseZIO[R, E, A, B](a: => ZIO[R, E, A])(pf: PartialFunction[A, ZIO[R, E, B]])(implicit
    trace: Trace
  ): ZIO[R, E, Option[B]] =
    ZIO.suspendSucceed(a.flatMap(whenCase(_)(pf)))

  /**
   * Runs an effect when the supplied `PartialFunction` matches for the given
   * effectful value, and discards the value, otherwise does nothing.
   */
  def whenCaseZIODiscard[R, E, A](a: => ZIO[R, E, A])(pf: PartialFunction[A, ZIO[R, E, Any]])(implicit
    trace: Trace
  ): ZIO[R, E, Unit] =
    suspendSucceed(a.flatMap(whenCaseDiscard(_)(pf)))

  /**
   * The moral equivalent of `if (p) exp` when `p` has side-effects
   */
  def whenZIO[R, E](p: => ZIO[R, E, Boolean]): ZIO.WhenZIO[R, E] =
    new ZIO.WhenZIO(() => p)

  /**
   * The moral equivalent of `if (p) exp: Unit` when `p` has side-effects
   */
  def whenZIODiscard[R, E](p: => ZIO[R, E, Boolean]): ZIO.WhenZIODiscard[R, E] =
    new ZIO.WhenZIODiscard(() => p)

  /**
   * Locally installs a supervisor and an effect that succeeds with all the
   * children that have been forked in the returned effect.
   */
  def withChildren[R, E, A](
    get: UIO[Chunk[Fiber.Runtime[Any, Any]]] => ZIO[R, E, A]
  )(implicit trace: Trace): ZIO[R, E, A] =
    Supervisor.track(true).flatMap { supervisor =>
      // Filter out the fiber id of whoever is calling this:
      get(supervisor.value.flatMap(children => ZIO.descriptor.map(d => children.filter(_.id != d.id))))
        .supervised(supervisor)
    }

  /**
   * Executes the specified workflow with the specified implementation of the
   * clock service.
   */
  def withClock[R, E, A <: Clock, B](clock: => A)(
    zio: => ZIO[R, E, B]
  )(implicit tag: Tag[A], trace: Trace): ZIO[R, E, B] =
    DefaultServices.currentServices.locallyWith(_.add(clock))(zio)

  /**
   * Sets the implementation of the clock service to the specified value and
   * restores it to its original value when the scope is closed.
   */
  def withClockScoped[A <: Clock](clock: => A)(implicit tag: Tag[A], trace: Trace): ZIO[Scope, Nothing, Unit] =
    DefaultServices.currentServices.locallyScopedWith(_.add(clock))

  /**
   * Executes the specified workflow with the specified configuration provider.
   */
  def withConfigProvider[R, E, A <: ConfigProvider, B](configProvider: => A)(
    zio: => ZIO[R, E, B]
  )(implicit tag: Tag[A], trace: Trace): ZIO[R, E, B] =
    DefaultServices.currentServices.locallyWith(_.add(configProvider))(zio)

  /**
   * Sets the configuration provider to the specified value and restores it to
   * its original value when the scope is closed.
   */
  def withConfigProviderScoped[A <: ConfigProvider](
    configProvider: => A
  )(implicit tag: Tag[A], trace: Trace): ZIO[Scope, Nothing, Unit] =
    DefaultServices.currentServices.locallyScopedWith(_.add(configProvider))

  /**
   * Executes the specified workflow with the specified implementation of the
   * console service.
   */
  def withConsole[R, E, A <: Console, B](console: => A)(
    zio: => ZIO[R, E, B]
  )(implicit tag: Tag[A], trace: Trace): ZIO[R, E, B] =
    DefaultServices.currentServices.locallyWith(_.add(console))(zio)

  /**
   * Sets the implementation of the console service to the specified value and
   * restores it to its original value when the scope is closed.
   */
  def withConsoleScoped[A <: Console](console: => A)(implicit tag: Tag[A], trace: Trace): ZIO[Scope, Nothing, Unit] =
    DefaultServices.currentServices.locallyScopedWith(_.add(console))

  /**
   * Executed this workflow with the specified logger added.
   */
  def withLogger[R, E, A <: ZLogger[String, Any], B](logger: => A)(
    zio: => ZIO[R, E, B]
  )(implicit tag: Tag[A], trace: Trace): ZIO[R, E, B] =
    FiberRef.currentLoggers.locallyWith(_ + logger)(zio)

  /**
   * Adds the specified logger and removes it when the scope is closed.
   */
  def withLoggerScoped[A <: ZLogger[String, Any]](
    logger: => A
  )(implicit tag: Tag[A], trace: Trace): ZIO[Scope, Nothing, Unit] =
    FiberRef.currentLoggers.locallyScopedWith(_ + logger)

  /**
   * Runs the specified effect with the specified maximum number of fibers for
   * parallel operators.
   */
  def withParallelism[R, E, A](n: => Int)(zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed(Parallelism.locally(Some(n))(zio))

  /**
   * Runs the specified effect with the specified maximum number of fibers for
   * parallel operators, but passes it a restore function that can be used to
   * restore the inherited parallelism from whatever region the effect is
   * composed into.
   */
  def withParallelismMask[R, E, A](n: => Int)(f: ZIO.ParallelismRestorer => ZIO[R, E, A])(implicit
    trace: Trace
  ): ZIO[R, E, A] =
    Parallelism.getWith {
      case Some(n0) => Parallelism.locally(Some(n))(f(ParallelismRestorer.MakeParallel(n0)))
      case None     => Parallelism.locally(Some(n))(f(ParallelismRestorer.MakeParallelUnbounded))
    }

  /**
   * Runs the specified effect with an unbounded maximum number of fibers for
   * parallel operators.
   */
  def withParallelismUnbounded[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
    ZIO.suspendSucceed(Parallelism.locally(None)(zio))

  /**
   * Runs the specified effect with an unbounded maximum number of fibers for
   * parallel operators, but passes it a restore function that can be used to
   * restore the inherited parallelism from whatever region the effect is
   * composed into.
   */
  def withParallelismUnboundedMask[R, E, A](f: ZIO.ParallelismRestorer => ZIO[R, E, A])(implicit
    trace: Trace
  ): ZIO[R, E, A] =
    Parallelism.getWith {
      case Some(n) => Parallelism.locally(None)(f(ParallelismRestorer.MakeParallel(n)))
      case None    => Parallelism.locally(None)(f(ParallelismRestorer.MakeParallelUnbounded))
    }

  /**
   * Executes the specified workflow with the specified implementation of the
   * random service.
   */
  def withRandom[R, E, A <: Random, B](random: => A)(
    zio: => ZIO[R, E, B]
  )(implicit tag: Tag[A], trace: Trace): ZIO[R, E, B] =
    DefaultServices.currentServices.locallyWith(_.add(random))(zio)

  /**
   * Sets the implementation of the random service to the specified value and
   * restores it to its original value when the scope is closed.
   */
  def withRandomScoped[A <: Random](random: => A)(implicit tag: Tag[A], trace: Trace): ZIO[Scope, Nothing, Unit] =
    DefaultServices.currentServices.locallyScopedWith(_.add(random))

  def withRuntimeFlagsScoped(update: RuntimeFlags.Patch)(implicit trace: Trace): ZIO[Scope, Nothing, Unit] =
    if (update == RuntimeFlags.Patch.empty) {
      ZIO.unit
    } else {
      ZIO.uninterruptible {
        ZIO.runtimeFlags.flatMap { runtimeFlags =>
          val updatedRuntimeFlags = RuntimeFlags.Patch.patch(update)(runtimeFlags)
          val revertRuntimeFlags  = RuntimeFlags.diff(updatedRuntimeFlags, runtimeFlags)
          ZIO.updateRuntimeFlags(update) *>
            ZIO.addFinalizer(ZIO.updateRuntimeFlags(revertRuntimeFlags)).unit
        }
      }
    }

  /**
   * Executes the specified workflow with the specified implementation of the
   * system service.
   */
  def withSystem[R, E, A <: System, B](system: => A)(
    zio: => ZIO[R, E, B]
  )(implicit tag: Tag[A], trace: Trace): ZIO[R, E, B] =
    DefaultServices.currentServices.locallyWith(_.add(system))(zio)

  /**
   * Sets the implementation of the system service to the specified value and
   * restores it to its original value when the scope is closed.
   */
  def withSystemScoped[A <: System](system: => A)(implicit tag: Tag[A], trace: Trace): ZIO[Scope, Nothing, Unit] =
    DefaultServices.currentServices.locallyScopedWith(_.add(system))

  /**
   * Returns an effect that yields to the runtime system, starting on a fresh
   * stack. Manual use of this method can improve fairness, at the cost of
   * overhead.
   */
  def yieldNow(implicit trace: Trace): UIO[Unit] = ZIO.YieldNow(trace, false)

  private[zio] def withFiberRuntime[R, E, A](
    onState: (Fiber.Runtime[E, A], Fiber.Status.Running) => ZIO[R, E, A]
  )(implicit trace: Trace): ZIO[R, E, A] =
    Stateful(trace, onState)

  private val _IdentityFn: Any => Any = (a: Any) => a

  private val _FailFn: Any => Exit[Any, Nothing] = a => Exit.Failure(Cause.fail(a))

  private val _SuccessFn: Any => Exit[Nothing, Any] = a => Exit.Success(a)

  private val _SecondFn: (Any, Any) => Any = (_: Any, b: Any) => b

  private[zio] def secondFn[A]: (Any, A) => A = _SecondFn.asInstanceOf[(Any, A) => A]

  private[zio] def identityFn[A]: A => A = _IdentityFn.asInstanceOf[A => A]

  private[zio] def failFn[E]: E => Exit[E, Nothing] = _FailFn.asInstanceOf[E => Exit[E, Nothing]]

  private[zio] def successFn[A]: A => Exit[Nothing, A] = _SuccessFn.asInstanceOf[A => Exit[Nothing, A]]

  private[zio] def partitionMap[A, A1, A2](
    iterable: Iterable[A]
  )(f: A => Either[A1, A2]): (Iterable[A1], Iterable[A2]) =
    iterable.foldRight((List.empty[A1], List.empty[A2])) { case (a, (es, bs)) =>
      f(a).fold(
        e => (e :: es, bs),
        b => (es, b :: bs)
      )
    }

  private[zio] val unitFn: Any => Unit    = (_: Any) => ()
  private val unitZIOFn: Any => UIO[Unit] = (_: Any) => Exit.unit

  implicit final class ZIOAutoCloseableOps[R, E, A <: AutoCloseable](private val io: ZIO[R, E, A]) extends AnyVal {

    /**
     * Like `acquireReleaseWith`, safely wraps a use and release of a resource.
     * This resource will get automatically closed, because it implements
     * `AutoCloseable`.
     */
    def acquireReleaseWithAuto[R1 <: R, E1 >: E, B](use: A => ZIO[R1, E1, B])(implicit
      trace: Trace
    ): ZIO[R1, E1, B] =
      acquireReleaseWith(io)(a => ZIO.succeed(a.close()))(use)

    /**
     * Like `withFinalizer, add a finalizer from AutoClosable.
     */
    def withFinalizerAuto(implicit trace: Trace): ZIO[R with Scope, E, A] =
      ZIO.acquireRelease(io)(a => ZIO.succeed(a.close()))
  }

  implicit final class ZIOBooleanOps[R, E](private val self: ZIO[R, E, Boolean]) extends AnyVal {

    /**
     * Returns the logical conjunction of the `Boolean` value returned by this
     * effect and the `Boolean` value returned by the specified effect. This
     * operator has "short circuiting" behavior so if the value returned by this
     * effect is false the specified effect will not be evaluated.
     */
    final def &&[R1 <: R, E1 >: E](
      that: => ZIO[R1, E1, Boolean]
    )(implicit trace: Trace): ZIO[R1, E1, Boolean] =
      self.flatMap(a => if (a) that else Exit.`false`)

    /**
     * Returns the logical conjunction of the `Boolean` value returned by this
     * effect and the `Boolean` value returned by the specified effect. This
     * operator has "short circuiting" behavior so if the value returned by this
     * effect is true the specified effect will not be evaluated.
     */
    final def ||[R1 <: R, E1 >: E](
      that: => ZIO[R1, E1, Boolean]
    )(implicit trace: Trace): ZIO[R1, E1, Boolean] =
      self.flatMap(a => if (a) Exit.`true` else that)
  }

  implicit final class ZioRefineToOrDieOps[R, E <: Throwable, A](private val self: ZIO[R, E, A]) extends AnyVal {

    /**
     * Keeps some of the errors, and terminates the fiber with the rest.
     */
    def refineToOrDie[E1 <: E: ClassTag](implicit ev: CanFail[E], trace: Trace): ZIO[R, E1, A] =
      self.refineOrDie { case e: E1 => e }
  }

  final class ProvideSomeLayer[R0, -R, +E, +A](private val self: ZIO[R, E, A]) extends AnyVal {
    def apply[E1 >: E, R1](
      layer: => ZLayer[R0, E1, R1]
    )(implicit
      ev: R0 with R1 <:< R,
      tagged: EnvironmentTag[R1],
      trace: Trace
    ): ZIO[R0, E1, A] =
      self.asInstanceOf[ZIO[R0 with R1, E, A]].provideLayer(ZLayer.environment[R0] ++ layer)
  }

  final class UpdateService[-R, +E, +A, M](private val self: ZIO[R, E, A]) extends AnyVal {
    def apply[R1 <: R with M](
      f: M => M
    )(implicit tag: Tag[M], trace: Trace): ZIO[R1, E, A] =
      self.provideSomeEnvironment(_.update(f))
  }

  final class UpdateServiceAt[-R, +E, +A, Service](private val self: ZIO[R, E, A]) extends AnyVal {
    def apply[R1 <: R with Map[Key, Service], Key](key: => Key)(
      f: Service => Service
    )(implicit tag: Tag[Map[Key, Service]], trace: Trace): ZIO[R1, E, A] =
      self.provideSomeEnvironment(_.updateAt(key)(f))
  }

  @implicitNotFound(
    "Pattern guards are only supported when the error type is a supertype of NoSuchElementException. However, your effect has ${E} for the error type."
  )
  abstract class CanFilter[+E] {
    def apply(t: NoSuchElementException): E
  }

  object CanFilter {
    implicit def canFilter[E >: NoSuchElementException]: CanFilter[E] =
      new CanFilter[E] {
        def apply(t: NoSuchElementException): E = t
      }
  }

  final class Grafter(private val scope: FiberScope) extends AnyVal {
    def apply[R, E, A](zio: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
      FiberRef.forkScopeOverride.locally(Some(scope))(zio)
  }

  final class IfZIO[R, E](private val b: () => ZIO[R, E, Boolean]) extends AnyVal {
    def apply[R1 <: R, E1 >: E, A](onTrue: => ZIO[R1, E1, A], onFalse: => ZIO[R1, E1, A])(implicit
      trace: Trace
    ): ZIO[R1, E1, A] =
      ZIO.suspendSucceed(b().flatMap(b => if (b) onTrue else onFalse))
  }

  final class UnlessZIO[R, E](private val b: () => ZIO[R, E, Boolean]) extends AnyVal {
    def apply[R1 <: R, E1 >: E, A](zio: => ZIO[R1, E1, A])(implicit trace: Trace): ZIO[R1, E1, Option[A]] =
      ZIO.suspendSucceed(b()).flatMap(b => if (b) Exit.none else zio.asSome)
  }

  final class UnlessZIODiscard[R, E](private val b: () => ZIO[R, E, Boolean]) extends AnyVal {
    def apply[R1 <: R, E1 >: E](zio: => ZIO[R1, E1, Any])(implicit trace: Trace): ZIO[R1, E1, Unit] =
      ZIO.suspendSucceed(b()).flatMap(b => if (b) Exit.unit else zio.unit)
  }

  final class WhenZIO[R, E](private val b: () => ZIO[R, E, Boolean]) extends AnyVal {
    def apply[R1 <: R, E1 >: E, A](zio: => ZIO[R1, E1, A])(implicit trace: Trace): ZIO[R1, E1, Option[A]] =
      ZIO.suspendSucceed(b()).flatMap(b => if (b) zio.asSome else Exit.none)
  }

  final class WhenZIODiscard[R, E](private val b: () => ZIO[R, E, Boolean]) extends AnyVal {
    def apply[R1 <: R, E1 >: E](zio: => ZIO[R1, E1, Any])(implicit trace: Trace): ZIO[R1, E1, Unit] =
      ZIO.suspendSucceed(b()).flatMap(b => if (b) zio.unit else Exit.unit)
  }

  final class TimeoutTo[-R, +E, +A, +B](self: ZIO[R, E, A], b: () => B) {
    def apply[B1 >: B](f: A => B1)(duration: => Duration)(implicit
      trace: Trace
    ): ZIO[R, E, B1] =
      ZIO.fiberIdWith { parentFiberId =>
        self.raceFibersWith[R, Nothing, E, Unit, B1](ZIO.sleep(duration).interruptible)(
          (winner, loser) =>
            winner.await.flatMap {
              case Exit.Success(a) =>
                winner.inheritAll *> loser.interruptAs(parentFiberId).as(f(a))
              case Exit.Failure(cause) =>
                winner.inheritAll *> loser.interruptAs(parentFiberId) *> Exit.failCause(cause)
            },
          (winner, loser) =>
            winner.await.flatMap {
              case _: Exit.Success[?] =>
                loser.inheritAll *> loser.interruptAs(parentFiberId).as(b())
              case Exit.Failure(cause) =>
                loser.inheritAll *> loser.interruptAs(parentFiberId) *> Exit.failCause(cause)
            },
          null,
          FiberScope.global
        )
      }
  }

  final class Acquire[-R, +E, +A](private val acquire: () => ZIO[R, E, A]) extends AnyVal {
    def apply[R1](release: A => URIO[R1, Any]): Release[R with R1, E, A] =
      new Release[R with R1, E, A](acquire, release)
  }
  final class Release[-R, +E, +A](acquire: () => ZIO[R, E, A], release: A => URIO[R, Any]) {
    def apply[R1 <: R, E1 >: E, B](use: A => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
      acquireReleaseExitWith(acquire())((a: A, _: Exit[E1, B]) => release(a))(use)
  }

  final class AcquireExit[-R, +E, +A](private val acquire: () => ZIO[R, E, A]) extends AnyVal {
    def apply[R1 <: R, E1 >: E, B](
      release: (A, Exit[E1, B]) => URIO[R1, Any]
    ): ReleaseExit[R1, E, E1, A, B] =
      new ReleaseExit(acquire, release)
  }
  final class ReleaseExit[-R, +E, E1, +A, B](
    acquire: () => ZIO[R, E, A],
    release: (A, Exit[E1, B]) => URIO[R, Any]
  ) {
    def apply[R1 <: R, E2 >: E <: E1, B1 <: B](use: A => ZIO[R1, E2, B1])(implicit
      trace: Trace
    ): ZIO[R1, E2, B1] =
      ZIO.uninterruptibleMask[R1, E2, B1](restore =>
        acquire().flatMap { a =>
          attemptOrDieZIO(restore(use(a))).exitWith { e =>
            attemptOrDieZIO(release(a, e)).foldCauseZIO(
              cause2 => Exit.failCause(e.foldExit(_ ++ cause2, _ => cause2)),
              _ => e
            )
          }
        }
      )
  }

  final class EnvironmentWithPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
    def apply[A](f: ZEnvironment[R] => A)(implicit trace: Trace): URIO[R, A] =
      ZIO.environmentWithZIO(env => ZIO.succeed(f(env)))
  }

  final class EnvironmentWithZIOPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
    def apply[R1 <: R, E, A](f: ZEnvironment[R] => ZIO[R1, E, A])(implicit trace: Trace): ZIO[R with R1, E, A] =
      FiberRef.currentEnvironment.getWith(env => f(env.asInstanceOf[ZEnvironment[R]]))
  }

  final class ScopedPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
    def apply[E, A](zio: => ZIO[Scope with R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
      Scope.make.flatMap(_.use[R](zio))
  }

  final class UsingPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
    def apply[R1, E, A, B](
      resource: ZIO[R with Scope, E, A]
    )(use: A => ZIO[R1, E, B])(implicit trace: Trace): ZIO[R with R1, E, B] =
      ZIO.scopedWith(_.extend[R](resource).flatMap(use))
  }

  final class ServiceAtPartiallyApplied[Service](private val dummy: Boolean = true) extends AnyVal {
    def apply[Key](
      key: => Key
    )(implicit tag: EnvironmentTag[Map[Key, Service]], trace: Trace): URIO[Map[Key, Service], Option[Service]] =
      ZIO.environmentWith(_.getAt(key))
  }

  final class ServiceWithPartiallyApplied[Service](private val dummy: Boolean = true) extends AnyVal {
    def apply[A](
      f: Service => A
    )(implicit tagged: Tag[Service], trace: Trace): ZIO[Service, Nothing, A] =
      ZIO.serviceWithZIO(service => ZIO.succeed(f(service)))
  }

  final class ServiceWithZIOPartiallyApplied[Service](private val dummy: Boolean = true) extends AnyVal {
    def apply[R <: Service, E, A](
      f: Service => ZIO[R, E, A]
    )(implicit
      tagged: Tag[Service],
      trace: Trace
    ): ZIO[R with Service, E, A] = {
      val tag = tagged.tag
      FiberRef.currentEnvironment.getWith(environment => f(environment.unsafe.get(tag)(Unsafe)))
    }
  }

  final class StatefulPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
    def apply[S, E, A](
      s: S
    )(zio: => ZIO[ZState[S] with R, E, A])(implicit tag: EnvironmentTag[S], trace: Trace): ZIO[R, E, A] =
      zio.provideSomeLayer[R](ZState.initial(s))
  }

  final class StatefulPatchPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal {
    def apply[State, Patch, E, A](
      state: State,
      differ: Differ[State, Patch]
    )(zio: => ZIO[ZState[State] with R, E, A])(implicit
      tag: EnvironmentTag[State],
      trace: Trace
    ): ZIO[R, E, A] =
      zio.provideSomeLayer[R](ZState.initialPatch(state, differ))
  }

  final class GetStateWithPartiallyApplied[S](private val dummy: Boolean = true) extends AnyVal {
    def apply[A](f: S => A)(implicit tag: EnvironmentTag[S], trace: Trace): ZIO[ZState[S], Nothing, A] =
      ZIO.serviceWithZIO(_.get.map(f))
  }

  final class LogSpan(val label: () => String) extends AnyVal {
    import zio.{LogSpan => ZioLogSpan}

    def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
      FiberRef.currentLogSpan.getWith { stack =>
        val instant = java.lang.System.currentTimeMillis()
        val logSpan = ZioLogSpan(label(), instant)

        FiberRef.currentLogSpan.locally(logSpan :: stack)(zio)
      }
  }

  final class LogAnnotate(val annotations: () => Set[LogAnnotation]) { self =>
    def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
      FiberRef.currentLogAnnotations.locallyWith(_ ++ annotations().map { case LogAnnotation(key, value) =>
        key -> value
      })(zio)
  }

  final class Tagged(val tags: () => Set[MetricLabel]) { self =>
    def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
      FiberRef.currentTags.locallyWith(_ ++ tags())(zio)
  }

  @inline
  private def succeedLeft[E, A]: E => UIO[Either[E, A]] =
    _succeedLeft.asInstanceOf[E => UIO[Either[E, A]]]

  private val _succeedLeft: Any => IO[Any, Either[Any, Any]] =
    e2 => Exit.succeed[Either[Any, Any]](Left(e2))

  @inline
  private def succeedRight[E, A]: A => UIO[Either[E, A]] =
    _succeedRight.asInstanceOf[A => UIO[Either[E, A]]]

  private val _succeedRight: Any => IO[Any, Either[Any, Any]] =
    a => Exit.succeed[Either[Any, Any]](Right(a))

  /**
   * A `ZIOConstructor[Input]` knows how to construct a `ZIO` value from an
   * input of type `Input`. This allows the type of the `ZIO` value constructed
   * to depend on `Input`. The constructed `ZIO` value is guaranteed not to
   * require any services not included in `Environment` or to be able to fail in
   * any ways not described by `Error`.
   */
  sealed trait ZIOConstructor[-Environment, +Error, In] {

    /**
     * The environment type of the `ZIO` value
     */
    type OutEnvironment >: Environment

    /**
     * The error type of the `ZIO` value.
     */
    type OutError <: Error

    /**
     * The success type the `ZIO` value.
     */
    type OutSuccess

    /**
     * Constructs a `ZIO` value from the specified input.
     */
    def make(input: => In)(implicit trace: Trace): ZIO[OutEnvironment, OutError, OutSuccess]
  }

  object ZIOConstructor extends ZIOConstructorLowPriority1 {

    /**
     * Constructs a `ZIO[Any, E, A]` from an `Either[Cause[E], A]`.
     */
    implicit def EitherCauseConstructor[E, A]: WithOut[Any, E, Either[Cause[E], A], Any, E, A] =
      new ZIOConstructor[Any, E, Either[Cause[E], A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Either[Cause[E], A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromEitherCause(input)
      }

    /**
     * Constructs a `ZIO[Any, E, A]` from an `Either[Cause[E], A]`.
     */
    implicit def EitherCauseLeftConstructor[E, A]: WithOut[Any, E, Left[Cause[E], A], Any, E, A] =
      new ZIOConstructor[Any, E, Left[Cause[E], A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Left[Cause[E], A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromEitherCause(input)
      }

    /**
     * Constructs a `ZIO[Any, E, A]` from an `Either[Cause[E], A]`.
     */
    implicit def EitherCauseRightConstructor[E, A]: WithOut[Any, E, Right[Cause[E], A], Any, E, A] =
      new ZIOConstructor[Any, E, Right[Cause[E], A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Right[Cause[E], A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromEitherCause(input)
      }

    /**
     * Constructs a `ZIO[Any, E, A]` from a `Fiber[E, A].`
     */
    implicit def FiberConstructor[E, A]: WithOut[Any, E, Fiber[E, A], Any, E, A] =
      new ZIOConstructor[Any, E, Fiber[E, A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Fiber[E, A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromFiber(input)
      }

    /**
     * Constructs a `ZIO[Any, E, A]` from a `Fiber[E, A].`
     */
    implicit def FiberRuntimeConstructor[E, A]: WithOut[Any, E, Fiber.Runtime[E, A], Any, E, A] =
      new ZIOConstructor[Any, E, Fiber.Runtime[E, A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Fiber.Runtime[E, A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromFiber(input)
      }

    /**
     * Constructs a `ZIO[Any, E, A]` from a `Fiber[E, A].`
     */
    implicit def FiberSyntheticConstructor[E, A]: WithOut[Any, E, Fiber.Synthetic[E, A], Any, E, A] =
      new ZIOConstructor[Any, E, Fiber.Synthetic[E, A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Fiber.Synthetic[E, A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromFiber(input)
      }

    /**
     * Constructs a `ZIO[R, E, A]` from a `ZIO[R, E, Fiber[E, A]]`.
     */
    implicit def FiberZIOConstructor[R, E1 <: E3, E2 <: E3, E3, A]: WithOut[R, E3, ZIO[R, E1, Fiber[E2, A]], R, E3, A] =
      new ZIOConstructor[R, E3, ZIO[R, E1, Fiber[E2, A]]] {
        type OutEnvironment = R
        type OutError       = E3
        type OutSuccess     = A
        def make(input: => ZIO[R, E1, Fiber[E2, A]])(implicit trace: Trace): ZIO[R, E3, A] =
          ZIO.fromFiberZIO(input)
      }

    /**
     * Constructs a `ZIO[R, E, A]` from a `ZIO[R, E, Fiber[E, A]]`.
     */
    implicit def FiberZIORuntimeConstructor[R, E1 <: E3, E2 <: E3, E3, A]
      : WithOut[R, E3, ZIO[R, E1, Fiber.Runtime[E2, A]], R, E3, A] =
      new ZIOConstructor[R, E3, ZIO[R, E1, Fiber.Runtime[E2, A]]] {
        type OutEnvironment = R
        type OutError       = E3
        type OutSuccess     = A
        def make(input: => ZIO[R, E1, Fiber.Runtime[E2, A]])(implicit trace: Trace): ZIO[R, E3, A] =
          ZIO.fromFiberZIO(input)
      }

    /**
     * Constructs a `ZIO[R, E, A]` from a `ZIO[R, E, Fiber[E, A]]`.
     */
    implicit def FiberZIOSyntheticConstructor[R, E1 <: E3, E2 <: E3, E3, A]
      : WithOut[R, E3, ZIO[R, E1, Fiber.Synthetic[E2, A]], R, E3, A] =
      new ZIOConstructor[R, E3, ZIO[R, E1, Fiber.Synthetic[E2, A]]] {
        type OutEnvironment = R
        type OutError       = E3
        type OutSuccess     = A
        def make(input: => ZIO[R, E1, Fiber.Synthetic[E2, A]])(implicit trace: Trace): ZIO[R, E3, A] =
          ZIO.fromFiberZIO(input)
      }

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from a `Future[A]`.
     */
    implicit def FutureConstructor[A, FutureLike[A] <: scala.concurrent.Future[A]]
      : WithOut[Any, Throwable, FutureLike[A], Any, Throwable, A] =
      new ZIOConstructor[Any, Throwable, FutureLike[A]] {
        type OutEnvironment = Any
        type OutError       = Throwable
        type OutSuccess     = A
        def make(input: => FutureLike[A])(implicit trace: Trace): ZIO[Any, Throwable, A] =
          ZIO.suspend(ZIO.fromFutureNow(input)(trace, Unsafe))
      }

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from a function `ExecutionContext
     * => Future[A]`.
     */
    implicit def FutureExecutionContextConstructor[A, FutureLike[A] <: scala.concurrent.Future[A]]
      : WithOut[Any, Throwable, scala.concurrent.ExecutionContext => FutureLike[A], Any, Throwable, A] =
      new ZIOConstructor[Any, Throwable, scala.concurrent.ExecutionContext => FutureLike[A]] {
        type OutEnvironment = Any
        type OutError       = Throwable
        type OutSuccess     = A
        def make(input: => (scala.concurrent.ExecutionContext => FutureLike[A]))(implicit
          trace: Trace
        ): ZIO[Any, Throwable, A] =
          ZIO.fromFuture(input)
      }

    /**
     * Constructs a `ZIO[Any, Option[Nothing], A]` from an `Option[A]`.
     */
    implicit def OptionConstructor[A]: WithOut[Any, Option[Nothing], Option[A], Any, Option[Nothing], A] =
      new ZIOConstructor[Any, Option[Nothing], Option[A]] {
        type OutEnvironment = Any
        type OutError       = Option[Nothing]
        type OutSuccess     = A
        def make(input: => Option[A])(implicit trace: Trace): ZIO[Any, Option[Nothing], A] =
          ZIO.fromOption(input)
      }

    /**
     * Constructs a `ZIO[Any, Option[Nothing], Nothing]` from a `None`.
     */
    implicit val OptionNoneConstructor: WithOut[Any, Option[Nothing], None.type, Any, Option[Nothing], Nothing] =
      new ZIOConstructor[Any, Option[Nothing], None.type] {
        type OutEnvironment = Any
        type OutError       = Option[Nothing]
        type OutSuccess     = Nothing
        def make(input: => None.type)(implicit trace: Trace): ZIO[Any, Option[Nothing], Nothing] =
          ZIO.fromOption(input)
      }

    /**
     * Constructs a `ZIO[Any, Option[Nothing], A]` from a `Some[A]`.
     */
    implicit def OptionSomeConstructor[A]: WithOut[Any, Option[Nothing], Some[A], Any, Option[Nothing], A] =
      new ZIOConstructor[Any, Option[Nothing], Some[A]] {
        type OutEnvironment = Any
        type OutError       = Option[Nothing]
        type OutSuccess     = A
        def make(input: => Some[A])(implicit trace: Trace): ZIO[Any, Option[Nothing], A] =
          ZIO.fromOption(input)
      }

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from a `Promise[A]`
     */
    implicit def PromiseScalaConstructor[A, PromiseLike[A] <: scala.concurrent.Promise[A]]
      : WithOut[Any, Throwable, PromiseLike[A], Any, Throwable, A] =
      new ZIOConstructor[Any, Throwable, PromiseLike[A]] {
        type OutEnvironment = Any
        type OutError       = Throwable
        type OutSuccess     = A
        def make(input: => PromiseLike[A])(implicit trace: Trace): ZIO[Any, Throwable, A] =
          ZIO.fromPromiseScala(input)
      }

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from a `Try[A]`.
     */
    implicit def TryConstructor[A]: WithOut[Any, Throwable, scala.util.Try[A], Any, Throwable, A] =
      new ZIOConstructor[Any, Throwable, scala.util.Try[A]] {
        type OutEnvironment = Any
        type OutError       = Throwable
        type OutSuccess     = A
        def make(input: => scala.util.Try[A])(implicit trace: Trace): ZIO[Any, Throwable, A] =
          ZIO.fromTry(input)
      }

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from a `Failure[A]`.
     */
    implicit def TryFailureConstructor[A]: WithOut[Any, Throwable, scala.util.Failure[A], Any, Throwable, A] =
      new ZIOConstructor[Any, Throwable, scala.util.Failure[A]] {
        type OutEnvironment = Any
        type OutError       = Throwable
        type OutSuccess     = A
        def make(input: => scala.util.Failure[A])(implicit trace: Trace): ZIO[Any, Throwable, A] =
          ZIO.fromTry(input)
      }

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from a `Success[A]`.
     */
    implicit def TrySuccessConstructor[A]: WithOut[Any, Throwable, scala.util.Success[A], Any, Throwable, A] =
      new ZIOConstructor[Any, Throwable, scala.util.Success[A]] {
        type OutEnvironment = Any
        type OutError       = Throwable
        type OutSuccess     = A
        def make(input: => scala.util.Success[A])(implicit trace: Trace): ZIO[Any, Throwable, A] =
          ZIO.fromTry(input)
      }
  }

  trait ZIOConstructorLowPriority1 extends ZIOConstructorLowPriority2 {

    /**
     * Constructs a `ZIO[Any, E, A]` from an `Either[E, A]`.
     */
    implicit def EitherConstructor[E, A]: WithOut[Any, E, Either[E, A], Any, E, A] =
      new ZIOConstructor[Any, E, Either[E, A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Either[E, A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromEither(input)
      }

    /**
     * Constructs a `ZIO[Any, E, A]]` from an `Either[E, A]`.
     */
    implicit def EitherLeftConstructor[E, A]: WithOut[Any, E, Left[E, A], Any, E, A] =
      new ZIOConstructor[Any, E, Left[E, A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Left[E, A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromEither(input)
      }

    /**
     * Constructs a `ZIO[Any, E, A]` from an `Either[E, A]`.
     */
    implicit def EitherRightConstructor[E, A]: WithOut[Any, E, Right[E, A], Any, E, A] =
      new ZIOConstructor[Any, E, Right[E, A]] {
        type OutEnvironment = Any
        type OutError       = E
        type OutSuccess     = A
        def make(input: => Right[E, A])(implicit trace: Trace): ZIO[Any, E, A] =
          ZIO.fromEither(input)
      }
  }

  trait ZIOConstructorLowPriority2 extends ZIOConstructorLowPriority3 {

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from an `A`.
     */
    implicit def AttemptConstructor[A]: WithOut[Any, Throwable, A, Any, Throwable, A] =
      new ZIOConstructor[Any, Throwable, A] {
        type OutEnvironment = Any
        type OutError       = Throwable
        type OutSuccess     = A
        def make(input: => A)(implicit trace: Trace): ZIO[Any, Throwable, A] =
          ZIO.attempt(input)
      }
  }

  trait ZIOConstructorLowPriority3 {

    /**
     * The type of the `ZIOConstructor` with the type of the `ZIO` value.
     */
    type WithOut[Environment, Error, In, OutEnvironment0, OutError0, OutSuccess0] =
      ZIOConstructor[Environment, Error, In] {
        type OutEnvironment = OutEnvironment0; type OutError = OutError0; type OutSuccess = OutSuccess0
      }

    /**
     * Constructs a `ZIO[Any, Throwable, A]` from an `A`.
     */
    implicit def SucceedConstructor[A]: WithOut[Any, Nothing, A, Any, Nothing, A] =
      new ZIOConstructor[Any, Nothing, A] {
        type OutEnvironment = Any
        type OutError       = Nothing
        type OutSuccess     = A
        def make(input: => A)(implicit trace: Trace): ZIO[Any, Nothing, A] =
          ZIO.succeed(input)
      }
  }

  private[zio] type Erased = ZIO[Any, Any, Any]

  private[zio] final case class FlatMap[R, E, A1, A2](
    trace: Trace,
    first: ZIO[R, E, A1],
    successK: A1 => ZIO[R, E, A2]
  ) extends Continuation
      with ZIO[R, E, A2]

  private[zio] sealed abstract class Continuation {
    def trace: Trace
  }
  object Continuation {
    def apply[R, E, A, B](f: A => ZIO[R, E, B])(implicit trace: Trace): Continuation =
      FlatMap[R, E, A, B](trace, null, f)
  }
  private[zio] case class FoldZIO[R, E1, E2, A1, A2](
    trace: Trace,
    first: ZIO[R, E1, A1],
    successK: A1 => ZIO[R, E2, A2],
    failureK: Cause[E1] => ZIO[R, E2, A2]
  ) extends Continuation
      with ZIO[R, E2, A2]
  private[zio] final case class Sync[A](trace: Trace, eval: () => A) extends ZIO[Any, Nothing, A]
  private[zio] final case class Async[R, E, A](
    trace: Trace,
    registerCallback: (ZIO[R, E, A] => Unit) => ZIO[R, E, A],
    blockingOn: () => FiberId
  ) extends ZIO[R, E, A]
  private[zio] final case class UpdateRuntimeFlags(trace: Trace, update: RuntimeFlags.Patch)
      extends Continuation
      with ZIO[Any, Nothing, Unit]

  private[zio] sealed trait UpdateRuntimeFlagsWithin[R, E, A] extends ZIO[R, E, A] {
    def update: RuntimeFlags.Patch
    def scope(oldRuntimeFlags: RuntimeFlags): ZIO[R, E, A]
  }
  private[zio] object UpdateRuntimeFlagsWithin extends UpdateRuntimeFlagsWithinPlatformSpecific {
    def apply[R, E, A](trace: Trace, update: RuntimeFlags.Patch, f: IntFunction[ZIO[R, E, A]]): DynamicNoBox[R, E, A] =
      DynamicNoBox(trace, update, f)

    @deprecated("Kept for bin-compat only", "2.1.7")
    final case class Interruptible[R, E, A](trace: Trace, effect: ZIO[R, E, A])
        extends UpdateRuntimeFlagsWithin[R, E, A] {
      def update: RuntimeFlags.Patch = RuntimeFlags.enableInterruption

      def scope(oldRuntimeFlags: RuntimeFlags): ZIO[R, E, A] = effect
    }
    @deprecated("Kept for bin-compat only", "2.1.7")
    final case class Uninterruptible[R, E, A](trace: Trace, effect: ZIO[R, E, A])
        extends UpdateRuntimeFlagsWithin[R, E, A] {
      def update: RuntimeFlags.Patch = RuntimeFlags.disableInterruption

      def scope(oldRuntimeFlags: RuntimeFlags): ZIO[R, E, A] = effect
    }
    @deprecated("Kept for bin-compat only", "2.1.7")
    final case class Dynamic[R, E, A](trace: Trace, update: RuntimeFlags.Patch, f: RuntimeFlags => ZIO[R, E, A])
        extends UpdateRuntimeFlagsWithin[R, E, A] {
      def scope(oldRuntimeFlags: RuntimeFlags): ZIO[R, E, A] = f(oldRuntimeFlags)
    }
    final case class DynamicNoBox[R, E, A](trace: Trace, update: RuntimeFlags.Patch, f: IntFunction[ZIO[R, E, A]])
        extends UpdateRuntimeFlagsWithin[R, E, A] {
      def scope(oldRuntimeFlags: RuntimeFlags): ZIO[R, E, A] = f(oldRuntimeFlags)
    }
  }
  private[zio] final case class GenerateStackTrace(trace: Trace) extends ZIO[Any, Nothing, StackTrace]
  private[zio] final case class Stateful[R, E, A](
    trace: Trace,
    onState: (Fiber.Runtime[E, A], Fiber.Status.Running) => ZIO[R, E, A]
  ) extends ZIO[R, E, A]
  private[zio] final case class WhileLoop[R, E, A](
    trace: Trace,
    check: () => Boolean,
    body: () => ZIO[R, E, A],
    process: A => Any
  ) extends ZIO[R, E, Unit]
  private[zio] final case class YieldNow(trace: Trace, forceAsync: Boolean) extends ZIO[Any, Nothing, Unit]

  sealed trait InterruptibilityRestorer {
    def apply[R, E, A](effect: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A]

    def isParentRegionInterruptible: Boolean

    final def isParentRegionUninterruptible: Boolean = !isParentRegionInterruptible

    final def parentInterruptStatus: InterruptStatus = InterruptStatus.fromBoolean(isParentRegionInterruptible)
  }
  object InterruptibilityRestorer {
    def apply(status: InterruptStatus): InterruptibilityRestorer =
      if (status.isInterruptible) MakeInterruptible
      else MakeUninterruptible

    def make(implicit trace: Trace): ZIO[Any, Nothing, InterruptibilityRestorer] =
      ZIO.checkInterruptible(status => ZIO.succeed(InterruptibilityRestorer(status)))

    case object MakeInterruptible extends InterruptibilityRestorer {
      def apply[R, E, A](effect: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        ZIO.UpdateRuntimeFlagsWithin(trace, RuntimeFlags.enableInterruption, _ => effect)

      def isParentRegionInterruptible: Boolean = true
    }
    case object MakeUninterruptible extends InterruptibilityRestorer {
      def apply[R, E, A](effect: => ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        ZIO.UpdateRuntimeFlagsWithin(trace, RuntimeFlags.disableInterruption, _ => effect)

      def isParentRegionInterruptible: Boolean = false
    }
  }

  sealed trait ParallelismRestorer {
    def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A]
  }

  object ParallelismRestorer {
    final case class MakeParallel(n: Int) extends ParallelismRestorer {
      def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        zio.withParallelism(n)
    }
    case object MakeParallelUnbounded extends ParallelismRestorer {
      def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        zio.withParallelismUnbounded
    }
  }

  sealed trait FinalizersRestorer {
    def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A]
  }

  object FinalizersRestorer {
    def apply(executionStrategy: ExecutionStrategy): FinalizersRestorer =
      executionStrategy match {
        case ExecutionStrategy.Sequential =>
          FinalizersRestorer.MakeSequential
        case ExecutionStrategy.Parallel =>
          FinalizersRestorer.MakeParallel
        case ExecutionStrategy.ParallelN(n) =>
          FinalizersRestorer.MakeParallelN(n)
      }

    case object Identity extends FinalizersRestorer {
      def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        zio
    }

    case object MakeParallel extends FinalizersRestorer {
      def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        zio.parallelFinalizers
    }

    final case class MakeParallelN(n: Int) extends FinalizersRestorer {
      def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        ZIO.environmentWithZIO[R] { environment =>
          environment.getDynamic[Scope] match {
            case None => zio
            case Some(scope) =>
              scope.executionStrategy match {
                case ExecutionStrategy.ParallelN(n) => zio
                case _                              => scope.forkWith(ExecutionStrategy.ParallelN(n)).flatMap(_.extend[R](zio))
              }
          }
        }
    }

    case object MakeSequential extends FinalizersRestorer {
      def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
        zio.sequentialFinalizers
    }
  }

  private[zio] val someFatal   = Some(LogLevel.Fatal)
  private[zio] val someError   = Some(LogLevel.Error)
  private[zio] val someWarning = Some(LogLevel.Warning)
  private[zio] val someInfo    = Some(LogLevel.Info)
  private[zio] val someDebug   = Some(LogLevel.Debug)
  private[zio] val someTrace   = Some(LogLevel.Trace)

  @deprecated("use succeed", "2.0.9")
  private[zio] def succeedNow[A](a: A): UIO[A] =
    succeed(a)(Trace.empty)

  private def collectAllParUnboundedDiscard[R, E, A](as: => Iterable[ZIO[R, E, A]])(implicit
    trace: Trace
  ): ZIO[R, E, Unit] =
    foreachParUnboundedDiscard(as)(ZIO.identityFn)

  private def foreachPar[R, E, A, B, Collection[+Element] <: Iterable[Element]](n: => Int)(
    as: Collection[A]
  )(
    fn: A => ZIO[R, E, B]
  )(implicit bf: BuildFrom[Collection[A], B, Collection[B]], trace: Trace): ZIO[R, E, Collection[B]] =
    ZIO.suspendSucceed {
      val array = Array.ofDim[AnyRef](as.size)
      val zioFunction: ((A, Int)) => ZIO[R, E, Any] = { case (a, i) =>
        fn(a).flatMap(b => ZIO.succeed(array(i) = b.asInstanceOf[AnyRef]))
      }
      foreachParDiscard(n)(as.zipWithIndex)(zioFunction) *>
        ZIO.succeed(bf.fromSpecific(as)(array.asInstanceOf[Array[B]]))
    }

  private def foreachParDiscard[R, E, A](
    n: => Int
  )(as0: => Iterable[A])(f: A => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.suspendSucceed {
      val as   = as0
      val size = as.size
      if (size == 0) Exit.unit
      else if (size == 1) f(as.head).unit
      else {

        def worker(queue: Queue[A]): ZIO[R, E, Unit] =
          queue.poll.flatMap {
            case Some(a) => f(a) *> worker(queue)
            case None    => Exit.unit
          }

        for {
          queue <- Queue.bounded[A](size)
          _     <- queue.offerAll(as)
          _     <- ZIO.collectAllParUnboundedDiscard(ZIO.replicate(n.min(size))(worker(queue)))
        } yield ()
      }
    }

  private def foreachParUnbounded[R, E, A, B, Collection[+Element] <: Iterable[Element]](
    as: Collection[A]
  )(
    f: A => ZIO[R, E, B]
  )(implicit bf: BuildFrom[Collection[A], B, Collection[B]], trace: Trace): ZIO[R, E, Collection[B]] =
    ZIO.suspendSucceed {
      val array = Array.ofDim[AnyRef](as.size)
      val zioFunction: ((A, Int)) => ZIO[R, E, Any] = { case (a, i) =>
        f(a).flatMap(b => ZIO.succeed(array(i) = b.asInstanceOf[AnyRef]))
      }
      foreachParUnboundedDiscard(as.zipWithIndex)(zioFunction) *>
        ZIO.succeed(bf.fromSpecific(as)(array.asInstanceOf[Array[B]]))
    }

  private def foreachParUnboundedDiscard[R, E, A](
    as0: => Iterable[A]
  )(f: A => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
    ZIO.suspendSucceed {
      val as   = as0
      val size = as.size
      if (size == 0) Exit.unit
      else if (size == 1) f(as.head).unit
      else {
        ZIO.uninterruptibleMask { restore =>
          val promise = Promise.unsafe.make[Unit, Unit](FiberId.None)(Unsafe)
          val ref     = new java.util.concurrent.atomic.AtomicInteger(size)
          ZIO
            .withFiberRuntime[R, Nothing, Iterable[Fiber.Runtime[E, Unit]]] { (fiber, _) =>
              ZIO.foreach(as) { a =>
                restore(f(a))
                  .foldCauseZIO(
                    cause =>
                      ZIO.withFiberRuntime[Any, E, Unit] { (childFiber, _) =>
                        ZIO.suspendSucceed {
                          childFiber.transferChildren(fiber.scope)
                          promise.fail(()) *> Exit.failCause(cause)
                        }
                      },
                    _ => {
                      ZIO.withFiberRuntime[Any, Nothing, Unit] { (childFiber, _) =>
                        childFiber.transferChildren(fiber.scope)
                        if (ref.decrementAndGet() == 0) {
                          promise.unsafe.done(Exit.unit)(Unsafe)
                        }
                        Exit.unit
                      }
                    }
                  )
                  .forkDaemon
              }
            }
            .flatMap { fibers =>
              restore(promise.await).foldCauseZIO(
                cause =>
                  ZIO
                    .foreachParUnbounded(fibers)(_.interrupt)
                    .flatMap(Exit.collectAllPar(_) match {
                      case Some(Exit.Failure(causes)) => Exit.failCause(cause.stripFailures && causes)
                      case _                          => Exit.failCause(cause.stripFailures)
                    }),
                _ => ZIO.foreachDiscard(fibers)(_.inheritAll)
              )
            }
        }
      }
    }
}

/**
 * An `Exit[E, A]` describes the result of executing an `IO` value. The result
 * is either succeeded with a value `A`, or failed with a `Cause[E]`.
 */
sealed trait Exit[+E, +A] extends ZIO[Any, E, A] { self =>
  import Exit._

  /**
   * Parallelly zips the this result with the specified result discarding the
   * first element of the tuple or else returns the failed `Cause[E1]`
   */
  final def &>[E1 >: E, B](that: Exit[E1, B]): Exit[E1, B] = zipRightWith(self, that)(_ && _)

  /**
   * Sequentially zips the this result with the specified result discarding the
   * first element of the tuple or else returns the failed `Cause[E1]`
   */
  final def *>[E1 >: E, B](that: Exit[E1, B]): Exit[E1, B] = zipRightWith(self, that)(_ ++ _)

  /**
   * Parallelly zips the this result with the specified result discarding the
   * second element of the tuple or else returns the failed `Cause[E1]`
   */
  final def <&[E1 >: E, B](that: Exit[E1, B]): Exit[E1, A] = zipRightWith(that, self)((l, r) => r && l)

  /**
   * Parallelly zips the this result with the specified result or else returns
   * the failed `Cause[E1]`
   */
  final def <&>[E1 >: E, B](that: Exit[E1, B])(implicit zippable: Zippable[A, B]): Exit[E1, zippable.Out] =
    zipWith(that)(zippable.zip(_, _), _ && _)

  /**
   * Sequentially zips the this result with the specified result discarding the
   * second element of the tuple or else returns the failed `Cause[E1]`
   */
  final def <*[E1 >: E, B](that: Exit[E1, B]): Exit[E1, A] = zipRightWith(that, self)((l, r) => r ++ l)

  /**
   * Sequentially zips the this result with the specified result or else returns
   * the failed `Cause[E1]`
   */
  final def <*>[E1 >: E, B](that: Exit[E1, B])(implicit zippable: Zippable[A, B]): Exit[E1, zippable.Out] =
    zipWith(that)(zippable.zip(_, _), _ ++ _)

  /**
   * Maps the success value of this effect to the specified constant value.
   */
  override final def as[B](b: => B)(implicit trace: Trace): ZIO[Any, E, B] =
    asExit(b)

  /**
   * Replaces the success value with the one provided.
   */
  final def asExit[B](b: B): Exit[E, B] = mapExit(_ => b)

  /**
   * Returns an option of the cause of failure.
   */
  final def causeOption: Option[Cause[E]] =
    self match {
      case Failure(cause) => Some(cause)
      case _              => None
    }

  private[zio] final def causeOrNull: Cause[E] =
    self match {
      case Failure(cause) => cause
      case _              => null.asInstanceOf[Cause[E]]
    }

  final def exists(p: A => Boolean): Boolean =
    foldExit(_ => false, p)

  override final def flatMap[R1, E1 >: E, B](k: A => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
    self match {
      case Success(a)     => k(a)
      case e @ Failure(_) => e
    }

  /**
   * Flat maps over the value type.
   */
  final def flatMapExit[E1 >: E, A1](f: A => Exit[E1, A1]): Exit[E1, A1] =
    self match {
      case Success(a)     => f(a)
      case e @ Failure(_) => e
    }

  /**
   * Flat maps over the value type.
   */
  final def flatMapExitZIO[E1 >: E, R, E2, A1](f: A => ZIO[R, E2, Exit[E1, A1]]): ZIO[R, E2, Exit[E1, A1]] =
    self match {
      case Success(a)     => f(a)
      case e @ Failure(_) => Exit.succeed(e)
    }

  /**
   * Flattens an Exit of an Exit into a single Exit value.
   */
  final def flattenExit[E1 >: E, B](implicit ev: A <:< Exit[E1, B]): Exit[E1, B] =
    Exit.flatten(self.mapExit(ev))

  /**
   * Folds over the failure value or the success value to yield an effect that
   * does not fail, but succeeds with the value returned by the left or right
   * function passed to `fold`.
   */
  override final def fold[B](failure: E => B, success: A => B)(implicit ev: CanFail[E], trace: Trace): UIO[B] =
    foldZIO(e => Exit.succeed(failure(e)), a => Exit.succeed(success(a)))

  /**
   * A more powerful version of `fold` that allows recovering from any kind of
   * failure except external interruption.
   */
  override final def foldCause[B](failure: Cause[E] => B, success: A => B)(implicit trace: Trace): UIO[B] =
    Exit.succeed(foldExit(failure, success))

  /**
   * A more powerful version of `foldZIO` that allows recovering from any kind
   * of failure except external interruption.
   */
  override final def foldCauseZIO[R, E2, B](
    failure: Cause[E] => ZIO[R, E2, B],
    success: A => ZIO[R, E2, B]
  )(implicit trace: Trace): ZIO[R, E2, B] =
    foldExitZIO(failure, success)

  /**
   * Folds over the value or cause.
   */
  final def foldExit[Z](failed: Cause[E] => Z, completed: A => Z): Z =
    self match {
      case Success(v)     => completed(v)
      case Failure(cause) => failed(cause)
    }

  /**
   * Sequentially zips the this result with the specified result or else returns
   * the failed `Cause[E1]`
   */
  final def foldExitZIO[R, E1, B](failed: Cause[E] => ZIO[R, E1, B], completed: A => ZIO[R, E1, B])(implicit
    trace: Trace
  ): ZIO[R, E1, B] =
    self match {
      case Success(v)     => completed(v)
      case Failure(cause) => failed(cause)
    }

  /**
   * Applies the function `f` to the successful result of the `Exit` and returns
   * the result in a new `Exit`.
   */
  final def foreach[R, E1 >: E, B](f: A => ZIO[R, E1, B])(implicit trace: Trace): ZIO[R, Nothing, Exit[E1, B]] =
    foldExit(c => Exit.succeed(failCause(c)), a => f(a).exit)

  /**
   * Retrieves the `A` if succeeded, or else returns the specified default `A`.
   */
  final def getOrElse[A1 >: A](orElse: Cause[E] => A1): A1 = self match {
    case Success(value) => value
    case Failure(cause) => orElse(cause)
  }

  final def getOrThrow()(implicit ev: E <:< Throwable, unsafe: Unsafe): A =
    getOrElse(cause => throw cause.squashTrace)

  final def getOrThrowFiberFailure()(implicit unsafe: Unsafe): A =
    getOrElse(c => throw FiberFailure(c))

  /**
   * Determines if the result is a failure.
   */
  final def isFailure: Boolean = !isSuccess

  /**
   * Determines if the result is interrupted.
   */
  final def isInterrupted: Boolean = self match {
    case _: Success[?] => false
    case Failure(c)    => c.isInterrupted
  }

  final def isInterruptedOnly: Boolean = self match {
    case _: Success[?] => false
    case Failure(c)    => c.isInterruptedOnly
  }

  /**
   * Determines if the result is a success.
   */
  final def isSuccess: Boolean =
    self.isInstanceOf[Success[?]]

  /**
   * Returns an effect whose success is mapped by the specified `f` function.
   */
  override final def map[B](f: A => B)(implicit trace: Trace): ZIO[Any, E, B] =
    mapExit(f)

  /**
   * Maps over the value type.
   */
  final def mapExit[A1](f: A => A1): Exit[E, A1] =
    self match {
      case Success(v)     => Exit.succeed(f(v))
      case e @ Failure(_) => e
    }

  /**
   * Returns an effect whose failure and success channels have been mapped by
   * the specified pair of functions, `f` and `g`.
   */
  override final def mapBoth[E2, B](f: E => E2, g: A => B)(implicit ev: CanFail[E], trace: Trace): ZIO[Any, E2, B] =
    mapBothExit(f, g)

  /**
   * Maps over both the error and value type.
   */
  final def mapBothExit[E1, A1](f: E => E1, g: A => A1): Exit[E1, A1] =
    mapErrorExit(f).mapExit(g)

  /**
   * Returns an effect with its error channel mapped using the specified
   * function. This can be used to lift a "smaller" error into a "larger" error.
   */
  override final def mapError[E2](f: E => E2)(implicit ev: CanFail[E], trace: Trace): ZIO[Any, E2, A] =
    mapErrorExit(f)

  /**
   * Maps over the error type.
   */
  final def mapErrorExit[E1](f: E => E1): Exit[E1, A] =
    self match {
      case e: Success[A] => e
      case Failure(c)    => failCause(c.map(f))
    }

  /**
   * Returns an effect with its full cause of failure mapped using the specified
   * function. This can be used to transform errors while preserving the
   * original structure of `Cause`.
   *
   * @see
   *   [[absorb]], [[sandbox]], [[catchAllCause]] - other functions for dealing
   *   with defects
   */
  override final def mapErrorCause[E2](h: Cause[E] => Cause[E2])(implicit trace: Trace): ZIO[Any, E2, A] =
    mapErrorCauseExit(h)

  /**
   * Maps over the cause type.
   */
  final def mapErrorCauseExit[E1](f: Cause[E] => Cause[E1]): Exit[E1, A] =
    self match {
      case e: Success[A] => e
      case Failure(c)    => Failure(f(c))
    }

  /**
   * Executes this effect and returns its value, if it succeeds, but otherwise
   * fails with the specified error.
   */
  override final def orElseFail[E1](e1: => E1)(implicit ev: CanFail[E], trace: Trace): ZIO[Any, E1, A] =
    orElseFailExit(e1)

  /**
   * Replaces the error value with the one provided.
   */
  final def orElseFailExit[E1](e1: => E1): Exit[E1, A] =
    mapErrorExit(_ => e1)

  /**
   * Converts the `Exit` to an `Either[Throwable, A]`, by wrapping the cause in
   * `FiberFailure` (if the result is failed).
   */
  final def toEither: Either[Throwable, A] = self match {
    case Success(value) => Right(value)
    case Failure(cause) => Left(FiberFailure(cause))
  }

  final def toTry(implicit ev: E <:< Throwable): scala.util.Try[A] =
    self match {
      case Success(value) => scala.util.Try(value)
      case Failure(cause) => scala.util.Failure(cause.squash)
    }

  final def trace: Trace = Trace.empty

  /**
   * Returns the effect resulting from mapping the success of this effect to
   * unit.
   */
  override final def unit(implicit trace: Trace): ZIO[Any, E, Unit] =
    unitExit

  /**
   * Discards the value.
   */
  final def unitExit: Exit[E, Unit] = asExit(())

  /**
   * Returns an untraced exit value.
   */
  final def untraced: Exit[E, A] = mapErrorCauseExit(_.untraced)

  /**
   * Named alias for `<*>`.
   */
  final def zip[E1 >: E, B](that: Exit[E1, B])(implicit zippable: Zippable[A, B]): Exit[E1, zippable.Out] =
    self <*> that

  /**
   * Named alias for `<*`.
   */
  final def zipLeft[E1 >: E, B](that: Exit[E1, B]): Exit[E1, A] = self <* that

  /**
   * Named alias for `<&>`.
   */
  final def zipPar[E1 >: E, B](that: Exit[E1, B])(implicit zippable: Zippable[A, B]): Exit[E1, zippable.Out] =
    self <&> that

  /**
   * Named alias for `<&`.
   */
  final def zipParLeft[E1 >: E, B](that: Exit[E1, B]): Exit[E1, A] = self <& that

  /**
   * Named alias for `&>`.
   */
  final def zipParRight[E1 >: E, B](that: Exit[E1, B]): Exit[E1, B] = self &> that

  /**
   * Named alias for `*>`.
   */
  final def zipRight[E1 >: E, B](that: Exit[E1, B]): Exit[E1, B] = self *> that

  /**
   * Zips this together with the specified result using the combination
   * functions.
   */
  final def zipWith[E1 >: E, B, C](that: Exit[E1, B])(
    f: (A, B) => C,
    g: (Cause[E], Cause[E1]) => Cause[E1]
  ): Exit[E1, C] =
    self match {
      case Success(l) =>
        that match {
          case Success(r)     => Exit.succeed(f(l, r))
          case f: Failure[E1] => f
        }
      case e @ Failure(c1) =>
        that match {
          case Failure(c2) => Exit.failCause(g(c1, c2))
          case _           => e
        }
    }

}

object Exit extends Serializable {

  final case class Success[+A](value: A)        extends Exit[Nothing, A]
  final case class Failure[+E](cause: Cause[E]) extends Exit[E, Nothing]

  def interrupt(id: FiberId): Exit[Nothing, Nothing] =
    failCause(Cause.interrupt(id))

  /**
   * Collects all Exits, and returns a List of the values if all Exits
   * succeeded, or combines the causes sequentially in case of failures. Returns
   * `None` in case the input iterable is empty
   *
   * @see
   *   [[collectAllParDiscard]] For a more performant variant that discard the
   *   successful values
   */
  def collectAll[E, A](exits: Iterable[Exit[E, A]]): Option[Exit[E, List[A]]] =
    collectAllWith(exits)(_ ++ _)

  /**
   * Collects all Exits, and succeeds with Unit if all Exits succeeded, or
   * combines the causes sequentially in case of failures.
   */
  def collectAllDiscard[E, A](exits: Iterable[Exit[E, A]]): Exit[E, Unit] =
    collectAllDiscardWith(exits)(_ ++ _)

  /**
   * Collects all Exits, and returns a List of the values if all Exits
   * succeeded, or combines the causes in parallel in case of failures. Returns
   * `None` in case the input iterable is empty
   *
   * @see
   *   [[collectAllParDiscard]] For a more performant variant that discard the
   *   successful values
   */
  def collectAllPar[E, A](exits: Iterable[Exit[E, A]]): Option[Exit[E, List[A]]] =
    collectAllWith(exits)(_ && _)

  /**
   * Collects all Exits, and succeeds with Unit if all Exits succeeded, or
   * combines the causes in parallel in case of failures.
   */
  def collectAllParDiscard[E, A](exits: Iterable[Exit[E, A]]): Exit[E, Unit] =
    collectAllDiscardWith(exits)(_ && _)

  private def collectAllWith[E, A](
    exits: Iterable[Exit[E, A]]
  )(
    combineError: (Cause[E], Cause[E]) => Cause[E]
  ): Option[Exit[E, List[A]]] =
    if (exits.isEmpty) None
    else {
      val builder = new ListBuffer[A]
      var cause   = null.asInstanceOf[Cause[E]]
      val it      = exits.iterator
      while (it.hasNext) {
        val head = it.next()
        head match {
          case Success(v) =>
            if (cause eq null) builder.append(v)
          case Failure(e) =>
            if (cause eq null) cause = e
            else cause = combineError(cause, e)
        }
      }
      if (cause eq null) Some(Success(builder.result()))
      else Some(Failure(cause))
    }

  private def collectAllDiscardWith[E, A](
    exits: Iterable[Exit[E, A]]
  )(
    combineError: (Cause[E], Cause[E]) => Cause[E]
  ): Exit[E, Unit] = {
    var cause = null.asInstanceOf[Cause[E]]
    val it    = exits.iterator
    while (it.hasNext) {
      val head = it.next()
      head match {
        case _: Success[?] => ()
        case Failure(e) =>
          if (cause eq null) cause = e
          else cause = combineError(cause, e)
      }
    }
    if (cause eq null) Exit.unit
    else Failure(cause)
  }

  def die(t: Throwable): Exit[Nothing, Nothing] =
    failCause(Cause.die(t))

  def fail[E](error: E): Exit[E, Nothing] =
    failCause(Cause.fail(error))

  def failCause[E](cause: Cause[E]): Exit[E, Nothing] =
    Failure(cause)

  def flatten[E, A](exit: Exit[E, Exit[E, A]]): Exit[E, A] =
    exit.flatMapExit(identity)

  def fromEither[E, A](e: Either[E, A]): Exit[E, A] =
    e.fold(fail, succeed)

  def fromOption[A](o: Option[A]): Exit[Unit, A] =
    o.fold[Exit[Unit, A]](fail(()))(succeed)

  def fromTry[A](t: scala.util.Try[A]): Exit[Throwable, A] =
    t match {
      case scala.util.Success(a) => succeed(a)
      case scala.util.Failure(t) => fail(t)
    }

  def succeed[A](a: A): Exit[Nothing, A] = Success(a)

  val unit: Exit[Nothing, Unit] = succeed(())

  private def zipRightWith[E, E1 >: E, A](left: Exit[E, Any], right: Exit[E1, A])(
    g: (Cause[E], Cause[E1]) => Cause[E1]
  ): Exit[E1, A] =
    left match {
      case _: Success[?] =>
        right match {
          case r: Success[A]  => r
          case f: Failure[E1] => f
        }
      case e @ Failure(c1) =>
        right match {
          case Failure(c2) => Exit.failCause(g(c1, c2))
          case _           => e
        }
    }

  private[zio] val `true`: Exit[Nothing, Boolean]           = Success(true)
  private[zio] val `false`: Exit[Nothing, Boolean]          = Success(false)
  private[zio] val none: Exit[Nothing, Option[Nothing]]     = Success(None)
  private[zio] val failNone: Exit[Option[Nothing], Nothing] = Failure(Cause.fail(None))

  private[zio] val empty: Exit[Nothing, Nothing] = Exit.failCause[Nothing](Cause.empty)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy