0
# Core Effects
1
2
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.
3
4
## Capabilities
5
6
### ZIO Effect Type
7
8
The core effect type representing computations that require environment R, may fail with E, or succeed with A.
9
10
```scala { .api }
11
/**
12
* A ZIO effect represents an async workflow that:
13
* - Requires environment R to execute
14
* - May fail with error type E
15
* - Succeeds with value type A
16
*/
17
sealed trait ZIO[-R, +E, +A] extends Product with Serializable
18
19
// Type aliases for common patterns
20
type IO[+E, +A] = ZIO[Any, E, A] // No requirements
21
type Task[+A] = ZIO[Any, Throwable, A] // May fail with Throwable
22
type RIO[-R, +A] = ZIO[R, Throwable, A] // Requires R, may fail
23
type UIO[+A] = ZIO[Any, Nothing, A] // Cannot fail
24
type URIO[-R, +A] = ZIO[R, Nothing, A] // Requires R, cannot fail
25
```
26
27
### Effect Construction
28
29
Create ZIO effects from values, failures, and side-effecting code.
30
31
```scala { .api }
32
/**
33
* Create an effect that always succeeds with the given value
34
*/
35
def succeed[A](a: => A): UIO[A]
36
37
/**
38
* Create an effect that always fails with the given error
39
*/
40
def fail[E](error: => E): IO[E, Nothing]
41
42
/**
43
* Convert side-effecting code into a ZIO effect
44
* Catches exceptions and converts them to typed failures
45
*/
46
def attempt[A](code: => A): Task[A]
47
48
/**
49
* Create an effect that succeeds with Unit
50
*/
51
val unit: UIO[Unit]
52
53
/**
54
* Create an effect that succeeds with None
55
*/
56
val none: UIO[Option[Nothing]]
57
58
/**
59
* Create an effect that terminates the fiber with a defect
60
*/
61
def die(t: => Throwable): UIO[Nothing]
62
63
/**
64
* Create an async effect from a callback registration function
65
*/
66
def async[R, E, A](register: (ZIO[R, E, A] => Unit) => Any): ZIO[R, E, A]
67
68
/**
69
* Convert a Scala Future to a ZIO effect
70
*/
71
def fromFuture[A](make: ExecutionContext => scala.concurrent.Future[A]): Task[A]
72
73
/**
74
* Convert an Either to a ZIO effect
75
*/
76
def fromEither[E, A](v: => Either[E, A]): IO[E, A]
77
```
78
79
**Usage Examples:**
80
81
```scala
82
import zio._
83
84
// Simple success
85
val greeting = ZIO.succeed("Hello, World!")
86
87
// Convert side-effecting code
88
val readFile = ZIO.attempt {
89
scala.io.Source.fromFile("config.txt").mkString
90
}
91
92
// Async callback integration
93
val timer = ZIO.async[Any, Nothing, Unit] { callback =>
94
val timer = new java.util.Timer()
95
timer.schedule(new java.util.TimerTask {
96
def run(): Unit = callback(ZIO.succeed(()))
97
}, 1000)
98
}
99
100
// From Future
101
val futureResult = ZIO.fromFuture { implicit ec =>
102
scala.concurrent.Future.successful(42)
103
}
104
```
105
106
### Effect Transformation
107
108
Transform the success value, error type, or environment requirements of effects.
109
110
```scala { .api }
111
/**
112
* Transform the success value of an effect
113
*/
114
def map[B](f: A => B): ZIO[R, E, B]
115
116
/**
117
* Chain effects together, with the second effect depending on the first
118
*/
119
def flatMap[R1 <: R, E1 >: E, B](k: A => ZIO[R1, E1, B]): ZIO[R1, E1, B]
120
121
/**
122
* Transform the error type of an effect
123
*/
124
def mapError[E2](f: E => E2): ZIO[R, E2, A]
125
126
/**
127
* Handle both success and failure cases
128
*/
129
def fold[B](failure: E => B, success: A => B): URIO[R, B]
130
131
/**
132
* Replace the success value with a constant
133
*/
134
def as[B](b: => B): ZIO[R, E, B]
135
136
/**
137
* Transform both success and error values
138
*/
139
def bimap[E2, A2](f: E => E2, g: A => A2): ZIO[R, E2, A2]
140
141
/**
142
* Provide part of the environment
143
*/
144
def provideSomeEnvironment[R0](f: ZEnvironment[R0] => ZEnvironment[R]): ZIO[R0, E, A]
145
146
/**
147
* Provide the entire environment
148
*/
149
def provideEnvironment(r: => ZEnvironment[R]): IO[E, A]
150
```
151
152
**Usage Examples:**
153
154
```scala
155
// Transform success value
156
val doubled = ZIO.succeed(21).map(_ * 2)
157
158
// Chain effects
159
val program = for {
160
line <- Console.readLine
161
_ <- Console.printLine(s"You entered: $line")
162
} yield ()
163
164
// Handle errors
165
val handled = readFile.fold(
166
error => s"Failed: $error",
167
content => s"Success: ${content.length} characters"
168
)
169
```
170
171
### Error Handling
172
173
Comprehensive error handling with recovery, fallback, and retry mechanisms.
174
175
```scala { .api }
176
/**
177
* Recover from all errors using a recovery function
178
*/
179
def catchAll[R1 <: R, E2, A1 >: A](h: E => ZIO[R1, E2, A1]): ZIO[R1, E2, A1]
180
181
/**
182
* Recover from specific errors matching a partial function
183
*/
184
def catchSome[R1 <: R, E1 >: E, A1 >: A](pf: PartialFunction[E, ZIO[R1, E1, A1]]): ZIO[R1, E1, A1]
185
186
/**
187
* Provide a fallback effect if this one fails
188
*/
189
def orElse[R1 <: R, E2, A1 >: A](that: => ZIO[R1, E2, A1]): ZIO[R1, E2, A1]
190
191
/**
192
* Retry this effect according to a schedule
193
*/
194
def retry[R1 <: R, S](policy: => Schedule[R1, E, S]): ZIO[R1, E, A]
195
196
/**
197
* Convert error to a defect (unrecoverable failure)
198
*/
199
def orDie(implicit ev: E <:< Throwable): URIO[R, A]
200
201
/**
202
* Convert defects to failures
203
*/
204
def sandbox: ZIO[R, Cause[E], A]
205
206
/**
207
* Ignore errors and return Option
208
*/
209
def option: URIO[R, Option[A]]
210
211
/**
212
* Convert errors to Either
213
*/
214
def either: URIO[R, Either[E, A]]
215
```
216
217
**Usage Examples:**
218
219
```scala
220
// Recover from errors
221
val recovered = readFile.catchAll { error =>
222
Console.printLineError(s"Failed to read file: $error") *>
223
ZIO.succeed("default content")
224
}
225
226
// Fallback chain
227
val withFallback =
228
readFile("primary.txt")
229
.orElse(readFile("backup.txt"))
230
.orElse(ZIO.succeed("default"))
231
232
// Retry with exponential backoff
233
val resilient = httpRequest.retry(
234
Schedule.exponential(100.millis) && Schedule.recurs(3)
235
)
236
```
237
238
### Concurrency Operations
239
240
Execute effects concurrently with fibers, racing, and parallel composition.
241
242
```scala { .api }
243
/**
244
* Fork this effect into a new fiber
245
*/
246
def fork: URIO[R, Fiber.Runtime[E, A]]
247
248
/**
249
* Race two effects, returning the result of whichever completes first
250
*/
251
def race[R1 <: R, E1 >: E, A1 >: A](that: => ZIO[R1, E1, A1]): ZIO[R1, E1, A1]
252
253
/**
254
* Execute two effects in parallel and combine results
255
*/
256
def zipPar[R1 <: R, E1 >: E, B](that: => ZIO[R1, E1, B]): ZIO[R1, E1, (A, B)]
257
258
/**
259
* Execute with a timeout, returning None if it takes too long
260
*/
261
def timeout(d: => Duration): ZIO[R, E, Option[A]]
262
263
/**
264
* Execute with a timeout, failing if it takes too long
265
*/
266
def timeoutFail[E1 >: E](e: => E1)(d: => Duration): ZIO[R, E1, A]
267
268
/**
269
* Make this effect interruptible
270
*/
271
def interruptible: ZIO[R, E, A]
272
273
/**
274
* Make this effect uninterruptible
275
*/
276
def uninterruptible: ZIO[R, E, A]
277
```
278
279
**Usage Examples:**
280
281
```scala
282
// Fork and join
283
val fiberProgram = for {
284
fiber <- heavyComputation.fork
285
_ <- otherWork
286
result <- fiber.join
287
} yield result
288
289
// Race multiple operations
290
val fastest =
291
httpRequest("server1")
292
.race(httpRequest("server2"))
293
.race(httpRequest("server3"))
294
295
// Parallel execution
296
val parallel =
297
fetchUser(userId).zipPar(fetchPreferences(userId))
298
299
// With timeout
300
val timedOut = longRunningTask.timeout(30.seconds)
301
```
302
303
### Collection Operations
304
305
Work with collections of effects using foreach, collectAll, and parallel variants.
306
307
```scala { .api }
308
/**
309
* Transform each element of a collection with an effect
310
*/
311
def foreach[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, E, List[B]]
312
313
/**
314
* Execute all effects in a collection sequentially
315
*/
316
def collectAll[R, E, A](effects: Iterable[ZIO[R, E, A]]): ZIO[R, E, List[A]]
317
318
/**
319
* Transform each element in parallel
320
*/
321
def foreachPar[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, E, List[B]]
322
323
/**
324
* Execute all effects in parallel
325
*/
326
def collectAllPar[R, E, A](effects: Iterable[ZIO[R, E, A]]): ZIO[R, E, List[A]]
327
328
/**
329
* Partition results based on success/failure
330
*/
331
def partition[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, Nothing, (List[E], List[B])]
332
333
/**
334
* Validate all elements, collecting failures
335
*/
336
def validatePar[R, E, A, B](as: Iterable[A])(f: A => ZIO[R, E, B]): ZIO[R, List[E], List[B]]
337
```
338
339
**Usage Examples:**
340
341
```scala
342
// Process each item
343
val processed = ZIO.foreach(userIds) { id =>
344
fetchUser(id).map(_.name)
345
}
346
347
// Execute multiple effects
348
val allResults = ZIO.collectAll(List(
349
fetchUser(1),
350
fetchUser(2),
351
fetchUser(3)
352
))
353
354
// Parallel processing with bounded parallelism
355
val parallelBatch = ZIO.foreachPar(largeDataSet.take(100)) { item =>
356
processItem(item)
357
}
358
```
359
360
### Conditional Execution
361
362
Execute effects conditionally based on predicates and optional values.
363
364
```scala { .api }
365
/**
366
* Execute effect only if condition is true
367
*/
368
def when[R, E, A](p: => Boolean)(zio: => ZIO[R, E, A]): ZIO[R, E, Option[A]]
369
370
/**
371
* Execute effect only if condition is false
372
*/
373
def unless[R, E, A](p: => Boolean)(zio: => ZIO[R, E, A]): ZIO[R, E, Option[A]]
374
375
/**
376
* Execute effect for Some values
377
*/
378
def foreach[R, E, A, B](option: Option[A])(f: A => ZIO[R, E, B]): ZIO[R, E, Option[B]]
379
380
/**
381
* Convert Option to ZIO, failing with provided error for None
382
*/
383
def fromOption[A](option: => Option[A]): IO[Unit, A]
384
```
385
386
**Usage Examples:**
387
388
```scala
389
// Conditional execution
390
val maybeLog = ZIO.when(debugMode) {
391
Console.printLine("Debug mode enabled")
392
}
393
394
// Process optional values
395
val processedUser = ZIO.foreach(maybeUser) { user =>
396
validateUser(user) *> saveUser(user)
397
}
398
```
399
400
### Environment Access
401
402
Access and manipulate the ZIO environment for dependency injection.
403
404
```scala { .api }
405
/**
406
* Access a service from the environment
407
*/
408
def service[A: Tag]: URIO[A, A]
409
410
/**
411
* Use a service from the environment
412
*/
413
def serviceWith[Service](f: Service => A): ZIO[Service, Nothing, A]
414
415
/**
416
* Use a service to create an effect
417
*/
418
def serviceWithZIO[Service]: ServiceWithZIOPartiallyApplied[Service]
419
420
/**
421
* Access the full environment
422
*/
423
def environment[R]: URIO[R, ZEnvironment[R]]
424
425
/**
426
* Access part of the environment
427
*/
428
def environmentWith[R](f: ZEnvironment[R] => A): URIO[R, A]
429
430
/**
431
* Create an effect that accesses environment to produce another effect
432
*/
433
def environmentWithZIO[R]: EnvironmentWithZIOPartiallyApplied[R]
434
```
435
436
**Usage Examples:**
437
438
```scala
439
// Access a service
440
val program = for {
441
config <- ZIO.service[AppConfig]
442
_ <- Console.printLine(s"App running on port ${config.port}")
443
} yield ()
444
445
// Use service with transformation
446
val userCount = ZIO.serviceWith[UserRepository](_.countUsers())
447
448
// Access multiple services
449
val businessLogic = for {
450
db <- ZIO.service[Database]
451
cache <- ZIO.service[Cache]
452
user <- db.findUser(userId)
453
_ <- cache.store(s"user:$userId", user)
454
} yield user
455
```
456
457
### Chunk Data Structures
458
459
High-performance immutable arrays optimized for ZIO operations with excellent memory efficiency and fast operations.
460
461
```scala { .api }
462
/**
463
* A Chunk represents an immutable, high-performance array-like collection
464
*/
465
sealed trait Chunk[+A] extends Iterable[A] {
466
/** Get element at index */
467
def apply(n: Int): A
468
469
/** Get the length of the chunk */
470
def length: Int
471
def size: Int = length
472
473
/** Check if chunk is empty */
474
def isEmpty: Boolean
475
def nonEmpty: Boolean = !isEmpty
476
477
/** Get first element if exists */
478
def headOption: Option[A]
479
480
/** Get last element if exists */
481
def lastOption: Option[A]
482
483
/** Transform each element */
484
def map[B](f: A => B): Chunk[B]
485
486
/** Filter elements */
487
def filter(f: A => Boolean): Chunk[A]
488
489
/** Fold/reduce elements */
490
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1
491
def foldLeft[B](z: B)(op: (B, A) => B): B
492
493
/** Concatenate with another chunk */
494
def ++[A1 >: A](that: Chunk[A1]): Chunk[A1]
495
496
/** Prepend element */
497
def +:[A1 >: A](elem: A1): Chunk[A1]
498
499
/** Append element */
500
def :+[A1 >: A](elem: A1): Chunk[A1]
501
502
/** Take first n elements */
503
def take(n: Int): Chunk[A]
504
505
/** Drop first n elements */
506
def drop(n: Int): Chunk[A]
507
508
/** Slice chunk */
509
def slice(from: Int, until: Int): Chunk[A]
510
511
/** Split at index */
512
def splitAt(n: Int): (Chunk[A], Chunk[A])
513
514
/** Convert to Array */
515
def toArray[A1 >: A: ClassTag]: Array[A1]
516
517
/** Convert to List */
518
def toList: List[A]
519
520
/** Convert to Vector */
521
def toVector: Vector[A]
522
}
523
524
/**
525
* Non-empty chunk with additional guarantees
526
*/
527
sealed trait NonEmptyChunk[+A] extends Chunk[A] {
528
/** Get first element (guaranteed to exist) */
529
def head: A
530
531
/** Get last element (guaranteed to exist) */
532
def last: A
533
534
/** Get tail (may be empty) */
535
def tail: Chunk[A]
536
537
/** Get init (may be empty) */
538
def init: Chunk[A]
539
540
/** Reduce without seed value */
541
def reduce[A1 >: A](op: (A1, A1) => A1): A1
542
}
543
544
/**
545
* Chunk construction methods
546
*/
547
object Chunk {
548
/** Empty chunk */
549
val empty: Chunk[Nothing]
550
551
/** Single element chunk */
552
def single[A](a: A): Chunk[A]
553
554
/** Create from varargs */
555
def apply[A](as: A*): Chunk[A]
556
557
/** Create from iterable */
558
def fromIterable[A](it: Iterable[A]): Chunk[A]
559
560
/** Create from array */
561
def fromArray[A](array: Array[A]): Chunk[A]
562
563
/** Create from ByteBuffer */
564
def fromByteBuffer(buffer: ByteBuffer): Chunk[Byte]
565
566
/** Fill with repeated value */
567
def fill[A](n: Int)(a: A): Chunk[A]
568
569
/** Generate using function */
570
def tabulate[A](n: Int)(f: Int => A): Chunk[A]
571
572
/** Create range of integers */
573
def range(start: Int, end: Int): Chunk[Int]
574
575
/** Unfold from seed value */
576
def unfold[S, A](s: S)(f: S => Option[(A, S)]): Chunk[A]
577
578
/** Iterate function */
579
def iterate[A](start: A, len: Int)(f: A => A): Chunk[A]
580
}
581
582
/**
583
* Non-empty chunk construction
584
*/
585
object NonEmptyChunk {
586
/** Create from first element and rest */
587
def apply[A](head: A, tail: A*): NonEmptyChunk[A]
588
589
/** Create from non-empty iterable */
590
def fromIterable[A](it: Iterable[A]): Option[NonEmptyChunk[A]]
591
592
/** Create single element */
593
def single[A](a: A): NonEmptyChunk[A]
594
}
595
```
596
597
**Usage Examples:**
598
599
```scala
600
import zio._
601
602
// Create chunks
603
val numbers = Chunk(1, 2, 3, 4, 5)
604
val fromList = Chunk.fromIterable(List("a", "b", "c"))
605
val repeated = Chunk.fill(5)("hello")
606
607
// Transform chunks
608
val doubled = numbers.map(_ * 2)
609
val evens = numbers.filter(_ % 2 == 0)
610
val sum = numbers.foldLeft(0)(_ + _)
611
612
// Combine chunks
613
val combined = Chunk(1, 2) ++ Chunk(3, 4)
614
val withExtra = 0 +: numbers :+ 6
615
616
// Work with non-empty chunks
617
val nonEmpty = NonEmptyChunk(1, 2, 3, 4)
618
val first = nonEmpty.head // guaranteed to exist
619
val reduced = nonEmpty.reduce(_ + _) // no seed needed
620
621
// High-performance operations
622
val large = Chunk.range(0, 1000000)
623
val sliced = large.slice(1000, 2000) // efficient slicing
624
val array = large.toArray // zero-copy when possible
625
626
// Integration with ZIO effects
627
val processChunk = ZIO.foreach(numbers) { n =>
628
Console.printLine(s"Processing: $n")
629
}
630
631
// Stream integration
632
val chunkStream = ZStream.fromChunk(numbers)
633
val collectedChunk = ZStream.range(1, 100).runCollect
634
```