How to handle Exceptions in Zio | Practical Implementation

zio
Reading Time: 3 minutes

In the previous blog ZIO Effects: How to Handle Errors? we have learned about How ZIO provides various means to handle and respond to failures.

In addition to this blog, we will see all the ways to handle the exceptions with its Implementation.

Either

  • If the effect fails, the result is in the form of Left
  • If the effect succeeds, the result is in the form of Right

Code Implementation with Example

import zio.{ExitCode, URIO, ZIO}

object HandlingExceptionUsingEither extends zio.App {

  val zEither: URIO[Any, Either[String, Nothing]] = ZIO.fail("I am failing you.").either

  val zMatch: ZIO[Any, Nothing, String] = zEither.map {
    case Right(success) => success
    case Left(exception) => s"Oops it failed with an exception: $exception"
  }

  override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
    (for {
      value <- zMatch
      _ <- zio.console.putStrLn(value)

    } yield ()).exitCode
  }
}

Output:
Oops it failed with an exception: I am failing you.
Code

Catching All Errors

This method is used to handle all the exceptions which might crash the program inappropriately.

Code Implementation with Example

import zio.{ExitCode, IO, URIO, ZIO}

object HandlingExceptionUsingCatchAll extends zio.App {

  val zFailed: IO[String, Nothing] = ZIO.fail("Some failure")

  val zCatchAll: ZIO[Any, Nothing, String] = zFailed.catchAll { _ =>
    ZIO.succeed("I will catch you from fail.")
  }


  override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
    (for {
      value <- zCatchAll
      _ <- zio.console.putStrLn(value)
    } yield ()).exitCode
  }
}

Output:
I will catch you from fail.
Code

Catching Some Errors

This method is used when we want to handle any specific type of exception.

Code Implementation with Example

import zio.{ExitCode, IO, URIO, ZIO}

import java.io.FileNotFoundException
import scala.io.{BufferedSource, Source}

object HandlingExceptionUsingCatchSome extends zio.App {

  val FileReader: ZIO[Any, Throwable, BufferedSource] = IO(Source.fromFile("PrimaryFile.txt")).catchSome {
    case _: FileNotFoundException => IO(Source.fromFile("BackUpFile.txt"))
  }

  override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
    (for {
      source <- FileReader
      value <- IO(source.getLines().mkString("\n"))
      _ <- zio.console.putStrLn(value)
    } yield ()).exitCode
  }
}

Output:
Hi, This is Back up File.
Code

Fallback

This method is used when we have to provide a backup. For example: Suppose I want to read a file “Primary.txt” but it is not available, So I will provide a backup as “Backup.txt”. Therefore, If the Primary file is not available use the Backup file.

Code Implementation with Example

import zio.{ExitCode, IO, URIO}

import scala.io.Source

object HandlingExceptionUsingOrElse extends zio.App{

  val fileReader = IO(Source.fromFile("Primary.txt")).orElse(IO(Source.fromFile("BackUpFile.txt")))

  override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
    (for{
      source <- fileReader
      value <- IO(source.getLines().mkString("\n"))
      _ <- zio.console.putStrLn(value)
    }yield ()).exitCode
  }
}

Output:
Hi, This is Back up File.
Code

Retrying

However, there are a number of useful methods on the ZIO data type for retrying failed effects.

This method is used to give some try before handling the exception. For example, in many cases program might take a few milliseconds to execute that piece of code which might fail in the first attempt but in another attempt might lead to success. However, If it still fails then we can handle it with any other method which is mentioned above.

Code Implementation with Example

import zio.{ExitCode, IO, URIO, ZIO}

import scala.io.{BufferedSource, Source}

object HandlingExceptionUsingRetry extends zio.App {

  val fileReader: ZIO[Any, Throwable, BufferedSource] = {
    IO(Source.fromFile("Primary.txt"))
      .retryN(3)
      .orElse(IO(Source.fromFile("BackUpFile.txt")))
  }

  override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
    (for{
      source <- fileReader
      value <- IO(source.getLines().mkString("\n"))
      _ <- zio.console.putStrLn(value)
    }yield ()).exitCode
  }
}

Output:
Hi, This is Back up File.
Code

Conclusion

Thank you guys for making it to the end of the blog I hope you gained some knowledge about How to handle exceptions along with their Implementation.

Reference

For more detailed information you can use this link: https://zio.dev/

knoldus

Written by 

Pallav is working as Software Consultant at Knoldus Inc. He is a technology enthusiast and a keen learner with more than 1.5 years of experience. He works as a Quality Catalyst in the organization. He has a good understanding of programming languages like Java and Scala. He likes to listen to songs and playing table tennis.

Leave a Reply