Skip to main content
zio-cats

Creating Effects

Step 2 of 15

Creating Effects

Both libraries provide similar constructors for creating effects from pure values and side effects.

Pure Values

Cats Effect
import cats.effect.IO

// Lift a pure value into IO
val pure: IO[Int] = IO.pure(42)

// Also works with any type
val pureString: IO[String] = IO.pure("hello")

IO.pure - lift pure value

ZIO
import zio._

// Lift a pure value into ZIO
val pure: UIO[Int] = ZIO.succeed(42)

// With explicit types
val pureExplicit: ZIO[Any, Nothing, Int] =
  ZIO.succeed(42)

ZIO.succeed - lift pure value

Side Effects (May Throw)

Cats Effect
import cats.effect.IO
import scala.io.Source

// Suspend a side effect that may throw
val readFile: IO[String] = IO.delay {
  Source.fromFile("data.txt").mkString
}

// IO.blocking for blocking operations
val blocking: IO[String] = IO.blocking {
  Source.fromFile("data.txt").mkString
}

IO.delay / IO.blocking - suspend side effects

ZIO
import zio._
import scala.io.Source

// Suspend a side effect that may throw
val readFile: Task[String] = ZIO.attempt {
  Source.fromFile("data.txt").mkString
}

// ZIO.attemptBlocking for blocking I/O
val blocking: Task[String] = ZIO.attemptBlocking {
  Source.fromFile("data.txt").mkString
}

ZIO.attempt / ZIO.attemptBlocking - suspend side effects

Failures

Cats Effect
import cats.effect.IO

// Raise an error (must be Throwable)
val fail: IO[Int] =
  IO.raiseError(new Exception("Something went wrong"))

// From a string (wrap in Exception)
val failStr: IO[Int] =
  IO.raiseError(new RuntimeException("oops"))

IO.raiseError - error must be Throwable

ZIO
import zio._

// Fail with any error type
val fail: IO[String, Int] =
  ZIO.fail("Something went wrong")

// Fail with Throwable
val failThrowable: Task[Int] =
  ZIO.fail(new Exception("oops"))

ZIO.fail - error can be any type

WARNING:

Cats Effect requires errors to be Throwable. ZIO allows any error type via the E parameter.

Unit Value

Cats Effect
import cats.effect.IO

// Unit value for chaining effects
val unit: IO[Unit] = IO.unit

// Often implicit in comprehensions
val program =
  for {
    _ <- IO.println("First")
    _ <- IO.println("Second")
  } yield ()

IO.unit - succeeds with Unit

ZIO
import zio._

// Unit value for chaining effects
val unit: UIO[Unit] = ZIO.unit

// Often implicit in comprehensions
val program =
  for {
    _ <- ZIO.attempt(println("First"))
    _ <- ZIO.attempt(println("Second"))
  } yield ()

ZIO.unit - succeeds with Unit

Never-Completing Effect

Cats Effect
import cats.effect.IO

// Effect that never completes
val never: IO[Nothing] = IO.never

// Useful for servers that run indefinitely
val runForever =
  IO.never

IO.never - never completes

ZIO
import zio._

// Effect that never completes
val never: ZIO[Any, Nothing, Nothing] = ZIO.never

// Useful for servers that run indefinitely
val runForever = ZIO.never

ZIO.never - never completes

TIP:

ZIO.never / IO.never are useful for keeping servers running indefinitely: server.useForever or ZIO.scoped(server *> ZIO.never).

From Scala Try

Cats Effect
import cats.effect.IO
import scala.util.{Try, Success, Failure}

// From Try (Success/Failure)
val fromTry: IO[Int] =
  IO.fromTry(Try(42))

// Try encapsulates Throwable
val fromTryFailure: IO[Int] =
  IO.fromTry(Failure(new Exception("oops")))

IO.fromTry - from Scala Try

ZIO
import zio._
import scala.util.{Try, Success, Failure}

// From Try (Success/Failure)
val fromTry: Task[Int] =
  ZIO.fromTry(Try(42))

// Try encapsulates Throwable
val fromTryFailure: Task[Int] =
  ZIO.fromTry(Failure(new Exception("oops")))

ZIO.fromTry - from Scala Try

TIP:

ZIO.fromTry converts Scala Try[A] to Task[A]. Since Try can only fail with Throwable, the error type is fixed to Throwable.

From Option/Either

Cats Effect
import cats.effect.IO

// From Option (None becomes error)
val fromOpt: IO[Int] =
  IO.fromOption(Some(42))(
    new NoSuchElementException("empty")
  )

// From Either
val fromEither: IO[Int] =
  IO.fromEither(Right(42))

IO.fromOption / IO.fromEither

ZIO
import zio._

// From Option (None becomes Option[Nothing] error)
val fromOpt: IO[Option[Nothing], Int] =
  ZIO.fromOption(Some(42))

// From Either
val fromEither: IO[String, Int] =
  ZIO.fromEither(Right(42))

ZIO.fromOption / ZIO.fromEither

Async Effects

Cats Effect
import cats.effect.IO
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

// From Future
val fromFuture: IO[Int] =
  IO.fromFuture(IO(Future.successful(42)))

// Async callback
val async: IO[Int] = IO.async_ { cb =>
  cb(Right(42))
}

IO.fromFuture / IO.async_

ZIO
import zio._
import scala.concurrent.Future

// From Future
val fromFuture: Task[Int] =
  ZIO.fromFuture(_ => Future.successful(42))

// Async callback
val async: Task[Int] = ZIO.async { cb =>
  cb(ZIO.succeed(42))
}

ZIO.fromFuture / ZIO.async

Next Steps

With effect creation covered, let's look at error handling patterns.

Next: Error Handling →