Repository: rcardin/raise4s (scala)

Issue:
Create a `run` function returning a union type `E | A`
Create a `run` function returning a union type `E | A`. The code should be something like the following:
```scala 3
val result: String | Int = Raise.run {
  Raise.raise("error")
}
```

Pull request:
Closes #33

Patch:
diff --git a/README.md b/README.md
index dbd8005..1d82a73 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,14 @@ The `fold` function “consumes” the context, creating a concrete instance of
 
 There are other flavors of the `fold` function. So, please, be sure to check them in the documentation.
 
+For those who are not interested in handling possible exceptions raised by a function, there is a more straightforward function available, called `run`:
+
+```scala 3
+val maybeUser: Error | User = Raise.run {
+  findUserById("42")
+}
+```
+
 Please be aware that any exception thrown inside the `Raise[E]` context will bubble up and not be transformed automatically into a logical typed error. What if we want to convert the exception into a typed error? For example, we want to convert the `IllegalArgumentException` into a `UserNotFound`. Well, we can do it using a function called `catching`:
 
 ```scala 3
diff --git a/src/main/scala/in/rcard/raise4s/Raise.scala b/src/main/scala/in/rcard/raise4s/Raise.scala
index dc9ddca..7769de0 100644
--- a/src/main/scala/in/rcard/raise4s/Raise.scala
+++ b/src/main/scala/in/rcard/raise4s/Raise.scala
@@ -312,7 +312,7 @@ object Raise {
     * @tparam Error
     *   The type of the logical error that can be raised by the `block` lambda
     */
-  //noinspection NoTailRecursionAnnotation
+  // noinspection NoTailRecursionAnnotation
   inline def fold[A, B, Error](
       inline block: Raise[Error] ?=> A,
       inline recover: (error: Error) => B,
@@ -999,4 +999,25 @@ object Raise {
     )(
       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`.
+    *
+    * <h2>Example</h2>
+    * {{{
+    * 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 = Runtime._run(block)
 }
diff --git a/src/main/scala/in/rcard/raise4s/Runtime.scala b/src/main/scala/in/rcard/raise4s/Runtime.scala
new file mode 100644
index 0000000..31329a6
--- /dev/null
+++ b/src/main/scala/in/rcard/raise4s/Runtime.scala
@@ -0,0 +1,7 @@
+package in.rcard.raise4s
+
+object Runtime {
+
+  private[raise4s] def _run[Error, A](block: Raise[Error] ?=> A): Error | A =
+    Raise.fold(block, identity, identity)
+}


Test patch:
diff --git a/src/test/scala/in/rcard/raise4s/RuntimeSpec.scala b/src/test/scala/in/rcard/raise4s/RuntimeSpec.scala
new file mode 100644
index 0000000..512a0af
--- /dev/null
+++ b/src/test/scala/in/rcard/raise4s/RuntimeSpec.scala
@@ -0,0 +1,25 @@
+package in.rcard.raise4s
+
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+class RuntimeSpec extends AnyFlatSpec with Matchers {
+  "Raise.run" should "return the happy path value is no error was risen" in {
+    val happyPathBlock: Int raises String = 42
+    val result: String | Int = Runtime._run(happyPathBlock)
+    result should be(42)
+  }
+
+  it should "return the error value if an error was risen" in {
+    val errorBlock: Int raises String = Raise.raise("error")
+    val result: String | Int = Runtime._run(errorBlock)
+    result should be("error")
+  }
+
+  it should "be transparent to any thrown exception" in {
+    val exceptionBlock: Int raises String = throw new RuntimeException("error")
+    assertThrows[RuntimeException] {
+      Runtime._run(exceptionBlock)
+    }
+  }
+}