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

in.rcard.raise4s.Raise.scala Maven / Gradle / Ivy

There is a newer version: 0.4.0
Show newest version
package in.rcard.raise4s

import in.rcard.raise4s

import java.lang.Runtime
import scala.annotation.targetName
import scala.util.Try
import scala.util.control.{ControlThrowable, NoStackTrace, NonFatal}

trait Raise[-Error]:
  def raise(e: Error): Nothing

private[raise4s] case class Raised[Error](original: Error)
    extends ControlThrowable
    with NoStackTrace

private[raise4s] class DefaultRaise extends Raise[Any]:
  def raise(e: Any): Nothing = throw Raised(e)

infix type raises[R, Error] = Raise[Error] ?=> R

/** Defines the main scope of the functions available on the `Raise` context.
  */
object Raise {

  extension [A](a: => A)
    /** Extensions method version of the [[Raise.catching]] function.
      */
    // noinspection NoTailRecursionAnnotation
    @targetName("catchingThis")
    inline def catching(inline catchBlock: Throwable => A): A = Raise.catching(() => a)(catchBlock)

  /** Raises a _logical failure_ of type `Error`. This function behaves like a return
    * statement, immediately short-circuiting and terminating the computation.
    *
    * __Alternatives:__ Common ways to raise errors include: [[ensure]], [[ensureNotNull]], and
    * [[Bind.value]]. Consider using them to make your code more concise and expressive.
    *
    * __Handling raised errors:__ Refer to [[recover]]. 

Example

* {{{ * val actual: String = fold( * { raise(MyError) }, * throwable => "Error: " + throwable.getMessage, * error => "Error: " + error, * value => "Value: " + value * ) * }}} * * @param e * An error of type `Error` that will short-circuit the computation. Behaves similarly to * _return_ or _throw_. * @param raise * The Raise context * @tparam Error * The type of the logical error */ inline def raise[Error](e: Error)(using raise: Raise[Error]): Nothing = raise.raise(e) /** Ensures that the `condition` is met; otherwise, [[Raise.raise]]s a logical failure of type * `Error`. * * In summary, this is a type-safe alternative to [[assert]], using the [[Raise]] API. * *

Example

* {{{ * val actual: Int = fold( * { ensure(42 < 0) { "error" }, * error => 43, * value => 42 * ) * actual should be(43) * }}} * * @param condition * The condition that must be true. * @param raise * A lambda that produces an error of type `Error` when the `condition` is false. * @param r * The Raise context * @tparam Error * The type of the logical error */ inline def ensure[Error](condition: Boolean)(raise: => Error)(using r: Raise[Error]): Unit = if !condition then r.raise(raise) /** Ensures that the `value` is not null; otherwise, [[Raise.raise]]s a logical failure of type * `Error`. * * In summary, this is a type-safe alternative to [[require]], using the [[Raise]] API. * *

Example

* {{{ * val actual: Int = fold( * { ensureNotNull(null) { "error" } }, * error => 43, * value => 42 * ) * actual should be(43) * }}} * * @param value * The value that must be non-null. * @param raise * A lambda that produces an error of type `Error` when the `value` is null. * @param r * The Raise context * @tparam B * The type of the value * @tparam Error * The type of the logical error * @return * The value if it is not null */ inline def ensureNotNull[B, Error](value: B)(raise: => Error)(using r: Raise[Error]): B = if value == null then r.raise(raise) else value /** Execute the [[Raise]] context function resulting in `A` or any _logical error_ of type * `Error`, and recover by providing a transform `Error` into a fallback value of type `A`.

* *

Example

* {{{ * val actual = recover({ raise("error") }) { * error => 43 * } * actual should be(43) * }}} * * @param block * The block to execute * @param recover * The function to transform the error into a fallback value * @tparam Error * The type of the error that can be raised and recovered * @tparam A * The type of the result of the `block` * @return * The result of the `block` or the fallback value */ inline def recover[Error, A](inline block: Raise[Error] ?=> A)(inline recover: Error => A): A = Raise.fold(block, ex => throw ex, recover, identity) /** Execute the [[Raise]] context function resulting in `A` or any _logical error_ of type * `Error`, and `recover` by providing a transform `Error` into a fallback value of type `A`, or * `catchBlock` any unexpected exceptions by providing a transform [[Throwable]] into a fallback * value of type `A`. * *

Example

* {{{ * val actual = recover( * { raise("error") }, * error => 43, * ex => 44 * ) * actual should be(43) * }}} * * @param block * The block to execute * @param recover * The function to transform the error into a fallback value * @param catchBlock * The function to transform the exception into a fallback value * @tparam Error * The type of the error that can be raised and recovered * @tparam A * The type of the result of the `block` * @return * The result of the `block`, the fallback value from the `recover` function, or the fallback * value from the `catchBlock` function */ inline def recover[Error, A]( inline block: Raise[Error] ?=> A, inline recover: Error => A, inline catchBlock: Throwable => A ): A = Raise.fold(block, catchBlock, recover, identity) /** Allows safely catching [[NonFatal]] exceptions without capturing exceptions like * [[OutOfMemoryError]] or [[VirtualMachineError]], etc. * *

Example

* {{{ * val actual = catching( * () => throw new RuntimeException("error"), * ex => 43 * ) * actual should be(43) * }}} * * @param block * The block to execute * @param catchBlock * The function to transform the exception into a fallback value * @tparam A * The type of the result of the `block` * @return * The result of the `block` or the fallback value */ inline def catching[A](inline block: () => A)(inline catchBlock: Throwable => A): A = try block() catch case NonFatal(e) => catchBlock(e) case ex => throw ex /** Execute the [[Raise]] context function resulting in `A` or any _logical error_ of type * `OtherError`, and transform any raised `OtherError` into `Error`, which is raised to the outer * [[Raise]]. * *

Example

* {{{ * val actual = either { * withError[Int, String, Int](s => s.length) { raise("error") } * } * actual should be(Left(5)) * }}} * * @param transform * The function to transform the `OtherError` into `Error` * @param block * The block to execute * @param r * The Raise context * @tparam Error * The type of the transformed logical error * @tparam OtherError * The type of the logical error that can be raised and transformed * @tparam A * The type of the result of the `block` * @return * The result of the `block` */ inline def withError[Error, OtherError, A](inline transform: OtherError => Error)( inline block: Raise[OtherError] ?=> A )(using r: Raise[Error]): A = recover(block) { otherError => r.raise(transform(otherError)) } /** The most general way to execute a computation using [[Raise]]. Depending on the outcome of the * `block`, one of the three continuations is run: * - success `transform` result of [A] to a value of [B]. * - raised `recover` from raised value of `Error` to a value of `B`. * - exception `catch` from [[Throwable]] by transforming value into `B`. * * This method should never be wrapped in `try`/`catch` as it will not throw any unexpected * errors, it will only result in fatal exceptions such as [[OutOfMemoryError]]. * *

Example

* {{{ * val actual: String = fold( * { 42 }, * throwable => "Error: " + throwable.getMessage, * error => "Error: " + error, * value => "Value: " + value * ) * actual shouldBe "Value: 42" * }}} * * @param block * The block of code to execute that can raise an a logical type error * @param catchBlock * The block of code to execute when a [[NonFatal]] exception is thrown * @param recover * The block of code to execute when a logical error is raised * @param transform * The block of code to execute when the block of code is executed successfully * @tparam A * The type of the result of the execution of `block` lambda * @tparam B * The type of the result of the `fold` method * @tparam Error * The type of the logical error that can be raised by the `block` lambda */ inline def fold[A, B, Error]( inline block: Raise[Error] ?=> A, inline catchBlock: (throwable: Throwable) => B, inline recover: (error: Error) => B, inline transform: (value: A) => B ): B = { given raise: Raise[Error] = new DefaultRaise try transform(block) catch case Raised(error) => recover(error.asInstanceOf[Error]) case NonFatal(e) => catchBlock(e) case e: Throwable => throw e } /** The most general way to execute a computation using [[Raise]]. Depending on the outcome of the * `block`, one of the two continuations is run: * - success `transform` result of `A` to a value of `B`. * - raised `recover` from raised value of `Error` to a value of `B`. * * This function re-throws any exceptions thrown within the [[Raise]] block. * *

Example

* {{{ * val actual: String = fold( * { 42 }, * error => "Error: " + error, * value => "Value: " + value * ) * actual shouldBe "Value: 42" * }}} * * @param block * The block of code to execute that can raise an a logical type error * @param recover * The block of code to execute when a logical error is raised * @param transform * The block of code to execute when the block of code is executed successfully * @tparam A * The type of the result of the execution of `block` lambda * @tparam B * The type of the result of the `fold` method * @tparam Error * The type of the logical error that can be raised by the `block` lambda */ // noinspection NoTailRecursionAnnotation inline def fold[A, B, Error]( inline block: Raise[Error] ?=> A, inline recover: (error: Error) => B, inline transform: (value: A) => B ): B = Raise.fold(block, ex => throw ex, recover, transform) /** Runs a computation `block` using [[Raise]], and return its outcome as [[Either]]. * - [[Right]] represents success, * - [[Left]] represents logical failure. * * This function re-throws any exceptions thrown within the [[Raise]] block. * *

Example

* {{{ * val one: Either[Nothing, Int] = Right(1) * val left: Either[String, Int] = Left("error") * val actual = either { * val x = one.bind() * val y = recover( * { * left.bind() * }, * { _ => 1 } * ) * x + y * } * }}} * * @param block * A computation that can raise errors of type `Error` * @tparam A * The type of the value returned by the computation * @tparam Error * The type of the logical error that can be raised by the computation * @return * An [[Either]] representing the outcome of the computation */ inline def either[A, Error](inline block: Raise[Error] ?=> A): Either[Error, A] = _either(block) /** Runs a computation `block` using [[Raise]], and return its outcome as [[Option]]. * - [[Some]] represents success, * - [[None]] represents logical failure. This function re-throws any exceptions thrown within * the [[Raise]] block. * *

Example

* {{{ * val some: Option[Int] = Some(1) * val none: Option[Int] = None * val actual = option { * val x = some.bind() * val y = recover({ none.bind() }, { _ => 1 }) * x + y * } * actual should be(Some(2)) * }}} * * @param block * A computation that can raise errors of type `None.type` * @tparam A * The type of the value returned by the computation * @return * An [[Option]] representing the outcome of the computation */ inline def option[A](inline block: Raise[None.type] ?=> A): Option[A] = _option(block) /** Runs a computation `block` using [[Raise]], and return its outcome as [[Try]]. * *

Example

* {{{ * val one: Try[Int] = Success(1) * val failure: Try[Int] = Failure(new Exception("error")) * val actual = asTry { * val x = one.bind() * val y = recover({ failure.bind() }, { _ => 1 }) * x + y * } * actual should be(Success(2)) * }}} * * @param block * A computation that can raise errors of type `Throwable` * @tparam A * The type of the value returned by the computation * @return * An [[Try]] representing the outcome of the computation */ inline def asTry[A](inline block: Raise[Throwable] ?=> A): Try[A] = _asTry(block) /** Accumulate the errors obtained by executing the `transform` over every element of `iterable`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.mapOrAccumulate(List(1, 2, 3, 4, 5)) { * _ + 1 * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual shouldBe List(2, 3, 4, 5, 6) * }}} * * @param iterable * The collection of elements to transform * @param transform * The transformation to apply to each element that can raise an error of type `Error` * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised * @tparam A * The type of the elements in the `iterable` * @tparam B * The type of the transformed elements * @return * A list of transformed elements */ def mapOrAccumulate[Error, A, B](iterable: Iterable[A])( transform: Raise[Error] ?=> A => B )(using r: Raise[List[Error]]): List[B] = _mapOrAccumulate(iterable)(transform) /** Transform every element of `iterable` using the given `transform`, or accumulate all the * occurred errors using `combine`. * *

Example

* {{{ * case class Errors(val errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = * Raise.mapOrAccumulate(List(1, 2, 3, 4, 5), combineErrors) { value => * if (value % 2 == 0) { * Raise.raise(Errors(List(value.toString))) * } else { * value * } * } * * val actual = Raise.fold( * block, * identity, * identity * ) * * actual shouldBe Errors(List("2", "4")) * }}} * * @param iterable * The collection of elements to transform * @param combine * The function to combine the errors * @param transform * The transformation to apply to each element that can raise an error of type `Error` * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised * @tparam A * The type of the elements in the `iterable` * @tparam B * The type of the transformed elements * @return * A list of transformed elements */ def mapOrAccumulate[Error, A, B](iterable: Iterable[A], combine: (Error, Error) => Error)( transform: Raise[Error] ?=> A => B )(using r: Raise[Error]): List[B] = _mapOrAccumulate(iterable, combine)(transform) /** Accumulate the errors from running `action1`, and `action2`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 } * ) { case (a, b) => * List(a, b) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B )(block: (A, B) => C)(using r: Raise[List[Error]]): C = _zipOrAccumulate(action1, action2, {}, {}, {}, {}, {}, {}, {}) { (a: A, b: B, _, _, _, _, _, _, _) => block(a, b) } /** Accumulate the errors from running `action1`, `action2`, and `action3`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 }, * { 3 } * ) { case (a, b, c) => * List(a, b, c) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C )( block: (A, B, C) => D )(using r: Raise[List[Error]]): D = _zipOrAccumulate(action1, action2, action3, {}, {}, {}, {}, {}, {}) { (a: A, b: B, c: C, _, _, _, _, _, _) => block(a, b, c) } /** Accumulate the errors from running `action1`, `action2`, `action3`, and `action4`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 }, * { 3 }, * { 4 } * ) { case (a, b, c, d) => * List(a, b, c, d) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D )( block: (A, B, C, D) => E )(using r: Raise[List[Error]]): E = _zipOrAccumulate(action1, action2, action3, action4, {}, {}, {}, {}, {}) { (a: A, b: B, c: C, d: D, _, _, _, _, _) => block(a, b, c, d) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, and `action5`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 } * ) { case (a, b, c, d, e) => * List(a, b, c, d, e) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E )( block: (A, B, C, D, E) => F )(using r: Raise[List[Error]]): F = _zipOrAccumulate(action1, action2, action3, action4, action5, {}, {}, {}, {}) { (a: A, b: B, c: C, d: D, e: E, _, _, _, _) => block(a, b, c, d, e) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, and * `action6`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 } * ) { case (a, b, c, d, e, f) => * List(a, b, c, d, e, f) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F )( block: (A, B, C, D, E, F) => G )(using r: Raise[List[Error]]): G = _zipOrAccumulate(action1, action2, action3, action4, action5, action6, {}, {}, {}) { (a: A, b: B, c: C, d: D, e: E, f: F, _, _, _) => block(a, b, c, d, e, f) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, * `action6`, and `action7`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 }, * { 7 } * ) { case (a, b, c, d, e, f, g) => * List(a, b, c, d, e, f, g) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6, 7)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param action7 * Code block to run on type `G` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the seventh code block * @tparam H * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G, H]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F, action7: Raise[Error] ?=> G )( block: (A, B, C, D, E, F, G) => H )(using r: Raise[List[Error]]): H = _zipOrAccumulate(action1, action2, action3, action4, action5, action6, action7, {}, {}) { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, _, _) => block(a, b, c, d, e, f, g) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, * `action6`, `action7`, and `action8`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 }, * { 7 }, * { 8 } * ) { case (a, b, c, d, e, f, g, h) => * List(a, b, c, d, e, f, g, h) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6, 7, 8)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param action7 * Code block to run on type `G` * @param action8 * Code block to run on type `H` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the seventh code block * @tparam H * The type of the result of the eighth code block * @tparam I * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G, H, I]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F, action7: Raise[Error] ?=> G, action8: Raise[Error] ?=> H )( block: (A, B, C, D, E, F, G, H) => I )(using r: Raise[List[Error]]): I = _zipOrAccumulate( action1, action2, action3, action4, action5, action6, action7, action8, {} ) { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, Unit) => block(a, b, c, d, e, f, g, h) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, * `action6`, `action7`, `action8`, and `action9`. * *

Example

* {{{ * val block: List[Int] raises List[String] = Raise.zipOrAccumulate( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 }, * { 7 }, * { 8 }, * { 9 } * ) { case (a, b, c, d, e, f, g, h, i) => * List(a, b, c, d, e, f, g, h, i) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6, 7, 8, 9)) * }}} * * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param action7 * Code block to run on type `G` * @param action8 * Code block to run on type `H` * @param action9 * Code block to run on type `I` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the seventh code block * @tparam H * The type of the result of the eighth code block * @tparam I * The type of the result of the ninth code block * @tparam J * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G, H, I, J]( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F, action7: Raise[Error] ?=> G, action8: Raise[Error] ?=> H, action9: Raise[Error] ?=> I )(block: (A, B, C, D, E, F, G, H, I) => J)(using r: Raise[List[Error]]): J = _zipOrAccumulate( action1, action2, action3, action4, action5, action6, action7, action8, action9 )( block ) // ZIP COMBINE /** Accumulate the errors from running `action1`, and `action2`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors)( * { 1 }, * { 2 } * ) { case (a, b) => * List(a, b) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2)) * }}} * * @param combine * The function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B )(block: (A, B) => C)(using r: Raise[Error]): C = _zipOrAccumulate(combine)(action1, action2, {}, {}, {}, {}, {}, {}, {}) { (a: A, b: B, _, _, _, _, _, _, _) => block(a, b) } /** Accumulate the errors from running `action1`, `action2`, and `action3`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors)( * { 1 }, * { 2 }, * { 3 } * ) { case (a, b, c) => * List(a, b, c) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3)) * }}} * * @param combine * The function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C )( block: (A, B, C) => D )(using r: Raise[Error]): D = _zipOrAccumulate(combine)(action1, action2, action3, {}, {}, {}, {}, {}, {}) { (a: A, b: B, c: C, _, _, _, _, _, _) => block(a, b, c) } /** Accumulate the errors from running `action1`, `action2`, `action3`, and `action4`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors) * { 1 }, * { 2 }, * { 3 }, * { 4 } * ) { case (a, b, c, d) => * List(a, b, c, d) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4)) * }}} * * @param combine * The function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D )( block: (A, B, C, D) => E )(using r: Raise[Error]): E = _zipOrAccumulate(combine)(action1, action2, action3, action4, {}, {}, {}, {}, {}) { (a: A, b: B, c: C, d: D, _, _, _, _, _) => block(a, b, c, d) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, and `action5`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors)( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 } * ) { case (a, b, c, d, e) => * List(a, b, c, d, e) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5)) * }}} * * @param combine * The function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E )( block: (A, B, C, D, E) => F )(using r: Raise[Error]): F = _zipOrAccumulate(combine)(action1, action2, action3, action4, action5, {}, {}, {}, {}) { (a: A, b: B, c: C, d: D, e: E, _, _, _, _) => block(a, b, c, d, e) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, and * `action6`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors)( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 } * ) { case (a, b, c, d, e, f) => * List(a, b, c, d, e, f) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6)) * }}} * * @param combine * The function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F )( block: (A, B, C, D, E, F) => G )(using r: Raise[Error]): G = _zipOrAccumulate(combine)(action1, action2, action3, action4, action5, action6, {}, {}, {}) { (a: A, b: B, c: C, d: D, e: E, f: F, _, _, _) => block(a, b, c, d, e, f) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, * `action6`, and `action7`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors)( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 }, * { 7 } * ) { case (a, b, c, d, e, f, g) => * List(a, b, c, d, e, f, g) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6, 7)) * }}} * * @param combine * The function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param action7 * Code block to run on type `G` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the seventh code block * @tparam H * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G, H](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F, action7: Raise[Error] ?=> G )( block: (A, B, C, D, E, F, G) => H )(using r: Raise[Error]): H = _zipOrAccumulate(combine)( action1, action2, action3, action4, action5, action6, action7, {}, {} ) { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, _, _) => block(a, b, c, d, e, f, g) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, * `action6`, `action7`, and `action8`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors)( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 }, * { 7 }, * { 8 } * ) { case (a, b, c, d, e, f, g, h) => * List(a, b, c, d, e, f, g, h) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6, 7, 8)) * }}} * * @param combine * The function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param action7 * Code block to run on type `G` * @param action8 * Code block to run on type `H` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the seventh code block * @tparam H * The type of the result of the eighth code block * @tparam I * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G, H, I](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F, action7: Raise[Error] ?=> G, action8: Raise[Error] ?=> H )( block: (A, B, C, D, E, F, G, H) => I )(using r: Raise[Error]): I = _zipOrAccumulate(combine)( action1, action2, action3, action4, action5, action6, action7, action8, {} ) { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, _) => block(a, b, c, d, e, f, g, h) } /** Accumulate the errors from running `action1`, `action2`, `action3`, `action4`, `action5`, * `action6`, `action7`, `action8`, and `action9`. * *

Example

* {{{ * case class Errors(errors: List[String]) * def combineErrors(error1: Errors, error2: Errors): Errors = * Errors(error1.errors ++ error2.errors) * * val block: List[Int] raises Errors = Raise.zipOrAccumulate(combineErrors)( * { 1 }, * { 2 }, * { 3 }, * { 4 }, * { 5 }, * { 6 }, * { 7 }, * { 8 }, * { 9 } * ) { case (a, b, c, d, e, f, g, h, i) => * List(a, b, c, d, e, f, g, h, i) * } * val actual = Raise.fold( * block, * error => fail(s"An error occurred: $error"), * identity * ) * actual should be(List(1, 2, 3, 4, 5, 6, 7, 8, 9)) * }}} * * @param combine * Function to combine the errors * @param action1 * Code block to run on type `A` * @param action2 * Code block to run on type `B` * @param action3 * Code block to run on type `C` * @param action4 * Code block to run on type `D` * @param action5 * Code block to run on type `E` * @param action6 * Code block to run on type `F` * @param action7 * Code block to run on type `G` * @param action8 * Code block to run on type `H` * @param action9 * Code block to run on type `I` * @param block * Function to run on the results of the code blocks * @param r * The Raise context * @tparam Error * The type of the logical error that can be raised by any code block * @tparam A * The type of the result of the first code block * @tparam B * The type of the result of the second code block * @tparam C * The type of the result of the third code block * @tparam D * The type of the result of the fourth code block * @tparam E * The type of the result of the fifth code block * @tparam F * The type of the result of the sixth code block * @tparam G * The type of the result of the seventh code block * @tparam H * The type of the result of the eighth code block * @tparam I * The type of the result of the ninth code block * @tparam J * The type of the result of the block function * @return * The result of the block function */ def zipOrAccumulate[Error, A, B, C, D, E, F, G, H, I, J](combine: (Error, Error) => Error)( action1: Raise[Error] ?=> A, action2: Raise[Error] ?=> B, action3: Raise[Error] ?=> C, action4: Raise[Error] ?=> D, action5: Raise[Error] ?=> E, action6: Raise[Error] ?=> F, action7: Raise[Error] ?=> G, action8: Raise[Error] ?=> H, action9: Raise[Error] ?=> I )(block: (A, B, C, D, E, F, G, H, I) => J)(using r: Raise[Error]): J = _zipOrAccumulate(combine)( action1, action2, action3, action4, action5, action6, action7, action8, action9 )( block ) /** Execute a block of code that can raise a logical error and return the result or the error as a * union type `Error | A`. * *

Example

* {{{ * val happyPathBlock: Int raises String = 42 * val result: String | Int = Runtime._run(happyPathBlock) * result should be(42) * }}} * * @param block * The block of code to execute that can raise an a logical type error * @tparam Error * The type of the logical error that can be raised by the `block` lambda * @tparam A * The type of the result of the execution of `block` lambda * @return * The result of the execution of the `block` lambda or the logical error */ def run[Error, A](block: Raise[Error] ?=> A): Error | A = raise4s.Runtime._run(block) }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy