Skip to main content
Version: 2.x

Converting Defects to Failures

Both ZIO#resurrect and ZIO#absorb are symmetrical opposite of the ZIO#orDie operator. The ZIO#orDie takes failures from the error channel and converts them into defects, whereas the ZIO#absorb and ZIO#resurrect take defects and convert them into failures:

trait ZIO[-R, +E, +A] {
def absorb(implicit ev: E IsSubtypeOfError Throwable): ZIO[R, Throwable, A]
def absorbWith(f: E => Throwable): ZIO[R, Throwable, A]
def resurrect(implicit ev1: E IsSubtypeOfError Throwable): ZIO[R, Throwable, A]
}

When to Use​

Use ZIO#absorb or ZIO#resurrect when you need to treat all defects uniformly as Throwable failures — for example, to pipe a defect-prone effect into code that handles Throwable through the typed error channel. Use ZIO#unrefine or ZIO#unrefineWith instead when you want to promote only specific defects to typed domain errors while leaving all other defects untyped. Prefer resurrect over absorb when fiber interruption should remain an interruption rather than being collapsed into a typed Throwable failure.

Below are examples of the ZIO#absorb and ZIO#resurrect operators:

import zio._

val effect1 =
ZIO.fail(new IllegalArgumentException("wrong argument")) // ZIO[Any, IllegalArgumentException, Nothing]
.orDie // ZIO[Any, Nothing, Nothing]
.absorb // ZIO[Any, Throwable, Nothing]
.refineToOrDie[IllegalArgumentException] // ZIO[Any, IllegalArgumentException, Nothing]

val effect2 =
ZIO.fail(new IllegalArgumentException("wrong argument")) // ZIO[Any, IllegalArgumentException , Nothing]
.orDie // ZIO[Any, Nothing, Nothing]
.resurrect // ZIO[Any, Throwable, Nothing]
.refineToOrDie[IllegalArgumentException] // ZIO[Any, IllegalArgumentException, Nothing]

So what is the difference between ZIO#absorb and ZIO#resurrect operators?

The ZIO#absorb can recover from both Die and Interruption causes. Using this operator we can absorb failures, defects and interruptions using ZIO#absorb operation. It attempts to convert all causes into a failure, throwing away all information about the cause of the error:

import zio._

object MainApp extends ZIOAppDefault {
val effect1 =
ZIO.dieMessage("Boom!") // ZIO[Any, Nothing, Nothing]
.absorb // ZIO[Any, Throwable, Nothing]
.ignore
val effect2 =
ZIO.interrupt // ZIO[Any, Nothing, Nothing]
.absorb // ZIO[Any, Throwable, Nothing]
.ignore

def run =
(effect1 <*> effect2)
.debug("application exited successfully")
}

The output would be as below:

application exited successfully: ()

Whereas, the ZIO#resurrect will only recover from Die causes:

import zio._

object MainApp extends ZIOAppDefault {
val effect1 =
ZIO
.dieMessage("Boom!") // ZIO[Any, Nothing, Nothing]
.resurrect // ZIO[Any, Throwable, Nothing]
.ignore
val effect2 =
ZIO.interrupt // ZIO[Any, Nothing, Nothing]
.resurrect // ZIO[Any, Throwable, Nothing]
.ignore

def run =
(effect1 <*> effect2)
.debug("couldn't recover from fiber interruption")
}

And, here is the output:

timestamp=2022-02-18T14:21:52.559872464Z level=ERROR thread=#zio-fiber-0 message="Exception in thread "zio-fiber-2" java.lang.InterruptedException: Interrupted by thread "zio-fiber-"
at <empty>.MainApp.effect2(MainApp.scala:10)
at <empty>.MainApp.effect2(MainApp.scala:11)
at <empty>.MainApp.effect2(MainApp.scala:12)
at <empty>.MainApp.run(MainApp.scala:15)
at <empty>.MainApp.run(MainApp.scala:16)"