Creating Effects
Both libraries provide similar constructors for creating effects from pure values and side effects.
Pure Values
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
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)
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
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
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
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
Cats Effect requires errors to be Throwable. ZIO allows any error type via the E parameter.
Unit Value
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
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
import cats.effect.IO
// Effect that never completes
val never: IO[Nothing] = IO.never
// Useful for servers that run indefinitely
val runForever =
IO.neverIO.never - never completes
import zio._
// Effect that never completes
val never: ZIO[Any, Nothing, Nothing] = ZIO.never
// Useful for servers that run indefinitely
val runForever = ZIO.neverZIO.never - never completes
ZIO.never / IO.never are useful for keeping servers running indefinitely: server.useForever or ZIO.scoped(server *> ZIO.never).
From Scala Try
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
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
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
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
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
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_
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.