Skip to content

Commit

Permalink
feat: Added the Raise effect (very tiny extract from the Raise4s libr…
Browse files Browse the repository at this point in the history
…ary)
  • Loading branch information
rcardin committed Jan 7, 2025
1 parent 9ddf864 commit aa12c76
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
32 changes: 32 additions & 0 deletions yaes-core/src/main/scala/in/rcard/yaes/Raise.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package in.rcard.yaes

import scala.util.control.{ControlThrowable, NoStackTrace}

trait TypedError[-E] {
def raise(error: E): Nothing
}

type Raise[E] = Effect[TypedError[E]]

private[yaes] case class Raised[E](original: E)
extends ControlThrowable
with NoStackTrace

private[yaes] class DefaultTypedError extends TypedError[Any]:
def raise(e: Any): Nothing = throw Raised(e)

object Raise {
def apply[E, A](block: => A): Raise[E] ?=> A = block

def raise[E](error: E)(using eff: Raise[E]): Nothing = eff.sf.raise(error)

def run[E, A](block: Raise[E] ?=> A): A | E = {
given eff: Effect[TypedError[E]] = new Effect(new DefaultTypedError)

try {
block(using eff)
} catch {
case Raised(e) => e.asInstanceOf[E]
}
}
}
47 changes: 47 additions & 0 deletions yaes-core/src/test/scala/in/rcard/yaes/RaiseSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package in.rcard.yaes

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class RaiseSpec extends AnyFlatSpec with Matchers {

"Raise effect" should "be able to raise a typed error" in {
val actualResult = Raise.run {
Raise {
Raise.raise("Error")
}
}

actualResult shouldBe "Error"
}

it should "be able to run a block that does not raise an error" in {
val actualResult = Raise.run {
Raise {
42
}
}

actualResult shouldBe 42
}

it should "not catch exceptions" in {
assertThrows[RuntimeException] {
Raise.run {
Raise { throw new RuntimeException("Boom!") }
}
}
}

it should "compose with other effects" in {
val actualResult: (Effect[SideEffect], Raise[String]) ?=> Int = for {
io <- IO { 42 }
result <- Raise {
if (io != 42) io
else Raise.raise("Boom!")
}
} yield result

IO.run { Raise.run { actualResult } } shouldBe "Boom!"
}
}

0 comments on commit aa12c76

Please sign in to comment.