scalaadvanced

Cats Effect IO Monad Basics

Use Cats Effect IO for pure functional effects: sequencing, error handling, resource management.

scala
import cats.effect.{IO, IOApp, Resource, ExitCode}
import cats.syntax.all.*
import scala.concurrent.duration.*

object MyApp extends IOApp.Simple:

  // Pure IO values
  val hello: IO[Unit] = IO.println("Hello, Cats Effect!")
  val answer: IO[Int] = IO.pure(42)
  val delayed: IO[String] = IO.sleep(100.millis) *> IO.pure("Done")

  // Sequencing with flatMap
  val program: IO[Unit] = for
    _    <- IO.println("Starting...")
    name <- IO.pure("Alice")
    _    <- IO.println(s"Hello, $name!")
    num  <- IO.delay(scala.util.Random.nextInt(100))
    _    <- IO.println(s"Random: $num")
  yield ()

  // Error handling
  val risky: IO[Int] = IO.raiseError(RuntimeException("Boom!"))
  val safe: IO[Int] = risky.handleErrorWith { e =>
    IO.println(s"Caught: ${e.getMessage}") *> IO.pure(0)
  }

  val attempted: IO[Either[Throwable, Int]] = risky.attempt

  // Resource management
  def openFile(name: String): Resource[IO, String] =
    Resource.make(
      IO.println(s"Opening $name") *> IO.pure(s"Handle($name)")
    )(
      handle => IO.println(s"Closing $handle")
    )

  val fileProgram: IO[Unit] =
    openFile("data.txt").use { handle =>
      IO.println(s"Reading from $handle")
    }

  // Parallel execution
  val task1: IO[String] = IO.sleep(100.millis) *> IO.pure("Result A")
  val task2: IO[String] = IO.sleep(100.millis) *> IO.pure("Result B")
  val parallel: IO[(String, String)] = (task1, task2).parTupled

  // Retry
  def retry[A](io: IO[A], attempts: Int, delay: FiniteDuration): IO[A] =
    io.handleErrorWith { e =>
      if attempts > 1 then
        IO.println(s"Retrying (${attempts - 1} left)...") *>
          IO.sleep(delay) *>
          retry(io, attempts - 1, delay)
      else IO.raiseError(e)
    }

  // Traverse
  val ids: List[Int] = (1 to 5).toList
  val fetched: IO[List[String]] = ids.traverse { id =>
    IO.sleep(50.millis) *> IO.pure(s"Data-$id")
  }

  val parFetched: IO[List[String]] = ids.parTraverse { id =>
    IO.sleep(50.millis) *> IO.pure(s"Data-$id")
  }

  def run: IO[Unit] = for
    _ <- program
    _ <- safe
    r <- attempted
    _ <- IO.println(s"Attempted: $r")
    _ <- fileProgram
    p <- parallel
    _ <- IO.println(s"Parallel: $p")
    d <- fetched
    _ <- IO.println(s"Sequential: $d")
    d <- parFetched
    _ <- IO.println(s"Parallel fetch: $d")
  yield ()

Use Cases

  • Pure functional effect management
  • Safe resource acquisition and release
  • Parallel and sequential effect composition

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.