or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

application.mdconcurrency.mdcore-effects.mddependency-injection.mderror-handling.mdindex.mdmetrics.mdresource-management.mdservices.mdstm.mdstreams.mdtesting.md
tile.json

core-effects.mddocs/

Core Effects

The ZIO effect system provides the foundational ZIO[R, E, A] type and operations for building functional, concurrent applications with comprehensive error handling and resource safety.

Capabilities

ZIO Effect Type

The core effect type representing computations that require environment R, may fail with E, or succeed with A.

/**
 * A ZIO effect represents an async workflow that:
 * - Requires environment R to execute
 * - May fail with error type E  
 * - Succeeds with value type A
 */
sealed trait ZIO[-R, +E, +A] extends Product with Serializable

// Type aliases for common patterns
type IO[+E, +A]   = ZIO[Any, E, A]         // No requirements
type Task[+A]     = ZIO[Any, Throwable, A] // May fail with Throwable
type RIO[-R, +A]  = ZIO[R, Throwable, A]   // Requires R, may fail
type UIO[+A]      = ZIO[Any, Nothing, A]   // Cannot fail
type URIO[-R, +A] = ZIO[R, Nothing, A]     // Requires R, cannot fail

Effect Construction

Create ZIO effects from values, failures, and side-effecting code.

/**
 * Create an effect that always succeeds with the given value
 */
def succeed[A](a: => A): UIO[A]

/**
 * Create an effect that always fails with the given error
 */
def fail[E](error: => E): IO[E, Nothing]

/**
 * Convert side-effecting code into a ZIO effect
 * Catches exceptions and converts them to typed failures
 */
def attempt[A](code: => A): Task[A]

/**
 * Create an effect that succeeds with Unit
 */
val unit: UIO[Unit]

/**
 * Create an effect that succeeds with None
 */
val none: UIO[Option[Nothing]]

/**
 * Create an effect that terminates the fiber with a defect
 */
def die(t: => Throwable): UIO[Nothing]

/**
 * Create an async effect from a callback registration function
 */
def async[R, E, A](register: (ZIO[R, E, A] => Unit) => Any): ZIO[R, E, A]

/**
 * Convert a Scala Future to a ZIO effect
 */
def fromFuture[A](make: ExecutionContext => scala.concurrent.Future[A]): Task[A]

/**
 * Convert an Either to a ZIO effect
 */
def fromEither[E, A](v: => Either[E, A]): IO[E, A]

Usage Examples:

import zio._

// Simple success
val greeting = ZIO.succeed("Hello, World!")

// Convert side-effecting code
val readFile = ZIO.attempt {
  scala.io.Source.fromFile("config.txt").mkString
}

// Async callback integration
val timer = ZIO.async[Any, Nothing, Unit] { callback =>
  val timer = new java.util.Timer()
  timer.schedule(new java.util.TimerTask {
    def run(): Unit = callback(ZIO.succeed(()))
  }, 1000)
}

// From Future
val futureResult = ZIO.fromFuture { implicit ec =>
  scala.concurrent.Future.successful(42)
}

Effect Transformation

Transform the success value, error type, or environment requirements of effects.

/**
 * Transform the success value of an effect
 */
def map[B](f: A => B): ZIO[R, E, B]

/**
 * Chain effects together, with the second effect depending on the first
 */
def flatMap[R1 <: R, E1 >: E, B](k: A => ZIO[R1, E1, B]): ZIO[R1, E1, B]

/**
 * Transform the error type of an effect
 */
def mapError[E2](f: E => E2): ZIO[R, E2, A]

/**
 * Handle both success and failure cases
 */
def fold[B](failure: E => B, success: A => B): URIO[R, B]

/**
 * Replace the success value with a constant
 */
def as[B](b: => B): ZIO[R, E, B]

/**
 * Transform both success and error values
 */
def bimap[E2, A2](f: E => E2, g: A => A2): ZIO[R, E2, A2]

/**
 * Provide part of the environment
 */
def provideSomeEnvironment[R0](f: ZEnvironment[R0] => ZEnvironment[R]): ZIO[R0, E, A]

/**
 * Provide the entire environment
 */
def provideEnvironment(r: => ZEnvironment[R]): IO[E, A]

Usage Examples:

// Transform success value
val doubled = ZIO.succeed(21).map(_ * 2)

// Chain effects
val program = for {
  line <- Console.readLine
  _    <- Console.printLine(s"You entered: $line")
} yield ()

// Handle errors
val handled = readFile.fold(
  error => s"Failed: $error",
  content => s"Success: ${content.length} characters"
)

Error Handling

Comprehensive error handling with recovery, fallback, and retry mechanisms.

/**
 * Recover from all errors using a recovery function
 */
def catchAll[R1 <: R, E2, A1 >: A](h: E => ZIO[R1, E2, A1]): ZIO[R1, E2, A1]

/**
 * Recover from specific errors matching a partial function
 */
def catchSome[R1 <: R, E1 >: E, A1 >: A](pf: PartialFunction[E, ZIO[R1, E1, A1]]): ZIO[R1, E1, A1]

/**
 * Provide a fallback effect if this one fails
 */
def orElse[R1 <: R, E2, A1 >: A](that: => ZIO[R1, E2, A1]): ZIO[R1, E2, A1]

/**
 * Retry this effect according to a schedule
 */
def retry[R1 <: R, S](policy: => Schedule[R1, E, S]): ZIO[R1, E, A]

/**
 * Convert error to a defect (unrecoverable failure)
 */
def orDie(implicit ev: E <:< Throwable): URIO[R, A]

/**
 * Convert defects to failures
 */
def sandbox: ZIO[R, Cause[E], A]

/**
 * Ignore errors and return Option
 */
def option: URIO[R, Option[A]]

/**
 * Convert errors to Either
 */
def either: URIO[R, Either[E, A]]

Usage Examples:

// Recover from errors
val recovered = readFile.catchAll { error =>
  Console.printLineError(s"Failed to read file: $error") *>
  ZIO.succeed("default content")
}

// Fallback chain
val withFallback = 
  readFile("primary.txt")
    .orElse(readFile("backup.txt"))
    .orElse(ZIO.succeed("default"))

// Retry with exponential backoff
val resilient = httpRequest.retry(
  Schedule.exponential(100.millis) && Schedule.recurs(3)
)

Concurrency Operations

Execute effects concurrently with fibers, racing, and parallel composition.

/**
 * Fork this effect into a new fiber
 */
def fork: URIO[R, Fiber.Runtime[E, A]]

/**
 * Race two effects, returning the result of whichever completes first
 */
def race[R1 <: R, E1 >: E, A1 >: A](that: => ZIO[R1, E1, A1]): ZIO[R1, E1, A1]

/**
 * Execute two effects in parallel and combine results
 */
def zipPar[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B]): ZIO[R1, E1, (A, B)]

/**
 * Execute with a timeout, returning None if it takes too long
 */
def timeout(d: => Duration): ZIO[R, E, Option[A]]

/**
 * Execute with a timeout, failing if it takes too long
 */
def timeoutFail[E1 >: E](e: => E1)(d: => Duration): ZIO[R, E1, A]

/**
 * Make this effect interruptible
 */
def interruptible: ZIO[R, E, A]

/**
 * Make this effect uninterruptible
 */
def uninterruptible: ZIO[R, E, A]

Usage Examples:

// Fork and join
val fiberProgram = for {
  fiber <- heavyComputation.fork
  _     <- otherWork
  result <- fiber.join
} yield result

// Race multiple operations
val fastest = 
  httpRequest("server1")
    .race(httpRequest("server2"))
    .race(httpRequest("server3"))

// Parallel execution
val parallel = 
  fetchUser(userId).zipPar(fetchPreferences(userId))

// With timeout
val timedOut = longRunningTask.timeout(30.seconds)

Collection Operations

Work with collections of effects using foreach, collectAll, and parallel variants.

/**
 * Transform each element of a collection with an effect
 */
def foreach[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, E, List[B]]

/**
 * Execute all effects in a collection sequentially
 */
def collectAll[R, E, A](effects: Iterable[ZIO[R, E, A]]): ZIO[R, E, List[A]]

/**
 * Transform each element in parallel
 */
def foreachPar[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, E, List[B]]

/**
 * Execute all effects in parallel
 */
def collectAllPar[R, E, A](effects: Iterable[ZIO[R, E, A]]): ZIO[R, E, List[A]]

/**
 * Partition results based on success/failure
 */
def partition[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, Nothing, (List[E], List[B])]

/**
 * Validate all elements, collecting failures
 */
def validatePar[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, List[E], List[B]]

Usage Examples:

// Process each item
val processed = ZIO.foreach(userIds) { id =>
  fetchUser(id).map(_.name)
}

// Execute multiple effects
val allResults = ZIO.collectAll(List(
  fetchUser(1),
  fetchUser(2), 
  fetchUser(3)
))

// Parallel processing with bounded parallelism
val parallelBatch = ZIO.foreachPar(largeDataSet.take(100)) { item =>
  processItem(item)
}

Conditional Execution

Execute effects conditionally based on predicates and optional values.

/**
 * Execute effect only if condition is true
 */
def when[R, E, A](p: => Boolean)(zio: => ZIO[R, E, A]): ZIO[R, E, Option[A]]

/**
 * Execute effect only if condition is false
 */
def unless[R, E, A](p: => Boolean)(zio: => ZIO[R, E, A]): ZIO[R, E, Option[A]]

/**
 * Execute effect for Some values
 */
def foreach[R, E, A, B](option: Option[A])(f: A => ZIO[R, E, B]): ZIO[R, E, Option[B]]

/**
 * Convert Option to ZIO, failing with provided error for None
 */
def fromOption[A](option: => Option[A]): IO[Unit, A]

Usage Examples:

// Conditional execution
val maybeLog = ZIO.when(debugMode) {
  Console.printLine("Debug mode enabled")
}

// Process optional values
val processedUser = ZIO.foreach(maybeUser) { user =>
  validateUser(user) *> saveUser(user)
}

Environment Access

Access and manipulate the ZIO environment for dependency injection.

/**
 * Access a service from the environment
 */
def service[A: Tag]: URIO[A, A]

/**
 * Use a service from the environment
 */
def serviceWith[Service](f: Service => A): ZIO[Service, Nothing, A]  

/**
 * Use a service to create an effect
 */
def serviceWithZIO[Service]: ServiceWithZIOPartiallyApplied[Service]

/**
 * Access the full environment
 */
def environment[R]: URIO[R, ZEnvironment[R]]

/**
 * Access part of the environment
 */
def environmentWith[R](f: ZEnvironment[R] => A): URIO[R, A]

/**
 * Create an effect that accesses environment to produce another effect
 */
def environmentWithZIO[R]: EnvironmentWithZIOPartiallyApplied[R]

Usage Examples:

// Access a service
val program = for {
  config <- ZIO.service[AppConfig]
  _      <- Console.printLine(s"App running on port ${config.port}")
} yield ()

// Use service with transformation
val userCount = ZIO.serviceWith[UserRepository](_.countUsers())

// Access multiple services
val businessLogic = for {
  db    <- ZIO.service[Database]
  cache <- ZIO.service[Cache]
  user  <- db.findUser(userId)
  _     <- cache.store(s"user:$userId", user)
} yield user

Chunk Data Structures

High-performance immutable arrays optimized for ZIO operations with excellent memory efficiency and fast operations.

/**
 * A Chunk represents an immutable, high-performance array-like collection
 */
sealed trait Chunk[+A] extends Iterable[A] {
  /** Get element at index */
  def apply(n: Int): A
  
  /** Get the length of the chunk */
  def length: Int
  def size: Int = length
  
  /** Check if chunk is empty */
  def isEmpty: Boolean
  def nonEmpty: Boolean = !isEmpty
  
  /** Get first element if exists */
  def headOption: Option[A]
  
  /** Get last element if exists */
  def lastOption: Option[A]
  
  /** Transform each element */
  def map[B](f: A => B): Chunk[B]
  
  /** Filter elements */
  def filter(f: A => Boolean): Chunk[A]
  
  /** Fold/reduce elements */
  def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1
  def foldLeft[B](z: B)(op: (B, A) => B): B
  
  /** Concatenate with another chunk */
  def ++[A1 >: A](that: Chunk[A1]): Chunk[A1]
  
  /** Prepend element */
  def +:[A1 >: A](elem: A1): Chunk[A1]
  
  /** Append element */
  def :+[A1 >: A](elem: A1): Chunk[A1]
  
  /** Take first n elements */
  def take(n: Int): Chunk[A]
  
  /** Drop first n elements */
  def drop(n: Int): Chunk[A]
  
  /** Slice chunk */
  def slice(from: Int, until: Int): Chunk[A]
  
  /** Split at index */
  def splitAt(n: Int): (Chunk[A], Chunk[A])
  
  /** Convert to Array */
  def toArray[A1 >: A: ClassTag]: Array[A1]
  
  /** Convert to List */
  def toList: List[A]
  
  /** Convert to Vector */
  def toVector: Vector[A]
}

/**
 * Non-empty chunk with additional guarantees
 */
sealed trait NonEmptyChunk[+A] extends Chunk[A] {
  /** Get first element (guaranteed to exist) */
  def head: A
  
  /** Get last element (guaranteed to exist) */
  def last: A
  
  /** Get tail (may be empty) */
  def tail: Chunk[A]
  
  /** Get init (may be empty) */
  def init: Chunk[A]
  
  /** Reduce without seed value */
  def reduce[A1 >: A](op: (A1, A1) => A1): A1
}

/**
 * Chunk construction methods
 */
object Chunk {
  /** Empty chunk */
  val empty: Chunk[Nothing]
  
  /** Single element chunk */
  def single[A](a: A): Chunk[A]
  
  /** Create from varargs */
  def apply[A](as: A*): Chunk[A]
  
  /** Create from iterable */
  def fromIterable[A](it: Iterable[A]): Chunk[A]
  
  /** Create from array */
  def fromArray[A](array: Array[A]): Chunk[A]
  
  /** Create from ByteBuffer */
  def fromByteBuffer(buffer: ByteBuffer): Chunk[Byte]
  
  /** Fill with repeated value */
  def fill[A](n: Int)(a: A): Chunk[A]
  
  /** Generate using function */
  def tabulate[A](n: Int)(f: Int => A): Chunk[A]
  
  /** Create range of integers */
  def range(start: Int, end: Int): Chunk[Int]
  
  /** Unfold from seed value */
  def unfold[S, A](s: S)(f: S => Option[(A, S)]): Chunk[A]
  
  /** Iterate function */
  def iterate[A](start: A, len: Int)(f: A => A): Chunk[A]
}

/**
 * Non-empty chunk construction
 */
object NonEmptyChunk {
  /** Create from first element and rest */
  def apply[A](head: A, tail: A*): NonEmptyChunk[A]
  
  /** Create from non-empty iterable */
  def fromIterable[A](it: Iterable[A]): Option[NonEmptyChunk[A]]
  
  /** Create single element */
  def single[A](a: A): NonEmptyChunk[A]
}

Usage Examples:

import zio._

// Create chunks
val numbers = Chunk(1, 2, 3, 4, 5)
val fromList = Chunk.fromIterable(List("a", "b", "c"))
val repeated = Chunk.fill(5)("hello")

// Transform chunks
val doubled = numbers.map(_ * 2)
val evens = numbers.filter(_ % 2 == 0)
val sum = numbers.foldLeft(0)(_ + _)

// Combine chunks
val combined = Chunk(1, 2) ++ Chunk(3, 4)
val withExtra = 0 +: numbers :+ 6

// Work with non-empty chunks
val nonEmpty = NonEmptyChunk(1, 2, 3, 4)
val first = nonEmpty.head  // guaranteed to exist
val reduced = nonEmpty.reduce(_ + _)  // no seed needed

// High-performance operations
val large = Chunk.range(0, 1000000)
val sliced = large.slice(1000, 2000)  // efficient slicing
val array = large.toArray  // zero-copy when possible

// Integration with ZIO effects
val processChunk = ZIO.foreach(numbers) { n =>
  Console.printLine(s"Processing: $n")
}

// Stream integration
val chunkStream = ZStream.fromChunk(numbers)
val collectedChunk = ZStream.range(1, 100).runCollect