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]
}

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)"