0
# IO Monad Operations
1
2
The IO monad is the core effect type in Cats Effect, providing referentially transparent side effect management with full monad operations, error handling, and concurrency support.
3
4
## Capabilities
5
6
### IO Construction
7
8
Core constructors for creating IO values from various sources.
9
10
```scala { .api }
11
/**
12
* Constructs IO values from pure values, side effects, and async operations
13
*/
14
object IO {
15
/** Lift a pure value into the IO context */
16
def pure[A](a: A): IO[A]
17
18
/** Suspend a side-effecting computation */
19
def delay[A](body: => A): IO[A]
20
21
/** Suspend an IO computation for stack safety */
22
def defer[A](thunk: => IO[A]): IO[A]
23
24
/** Create an IO from an async callback */
25
def async[A](k: (Either[Throwable, A] => Unit) => Unit): IO[A]
26
27
/** Create an IO that always fails with the given error */
28
def raiseError[A](e: Throwable): IO[A]
29
30
/** Create an IO that never completes */
31
def never[A]: IO[Nothing]
32
33
/** Create an IO from a Future */
34
def fromFuture[A](fa: IO[Future[A]]): IO[A]
35
36
/** Create an IO from an Either */
37
def fromEither[A](e: Either[Throwable, A]): IO[A]
38
39
/** Create an IO from an Option, failing with NoSuchElementException if None */
40
def fromOption[A](o: Option[A]): IO[A]
41
42
/** Create an IO from an Option with custom error */
43
def fromOption[A](option: Option[A])(orElse: => Throwable): IO[A]
44
45
/** Create an IO from a Try */
46
def fromTry[A](t: Try[A]): IO[A]
47
48
/** Create an IO from an Eval */
49
def eval[A](fa: Eval[A]): IO[A]
50
51
/** Sleep for the specified duration */
52
def sleep(duration: FiniteDuration)(implicit timer: Timer[IO]): IO[Unit]
53
54
/** Create an async IO with pure registration */
55
def asyncF[A](k: (Either[Throwable, A] => Unit) => IO[Unit]): IO[A]
56
57
/** Create a cancelable IO */
58
def cancelable[A](k: (Either[Throwable, A] => Unit) => CancelToken[IO]): IO[A]
59
60
/** Create a ContextShift */
61
def contextShift(ec: ExecutionContext): ContextShift[IO]
62
63
/** Get current fiber trace */
64
def trace: IO[IOTrace]
65
66
/** Create a unit IO */
67
val unit: IO[Unit]
68
69
/** Create an empty Option IO */
70
def none[A]: IO[Option[A]]
71
72
/** Conditional execution */
73
def whenA(cond: Boolean)(action: => IO[Unit]): IO[Unit]
74
75
/** Inverse conditional execution */
76
def unlessA(cond: Boolean)(action: => IO[Unit]): IO[Unit]
77
78
/** Conditional error raising */
79
def raiseWhen(cond: Boolean)(e: => Throwable): IO[Unit]
80
81
/** Inverse conditional error raising */
82
def raiseUnless(cond: Boolean)(e: => Throwable): IO[Unit]
83
}
84
```
85
86
**Usage Examples:**
87
88
```scala
89
import cats.effect._
90
import scala.concurrent.duration._
91
92
// Pure values
93
val pureValue = IO.pure(42)
94
95
// Side effects
96
val sideEffect = IO.delay {
97
println("Hello, World!")
98
"result"
99
}
100
101
// Deferred computation
102
val deferred = IO.defer {
103
if (math.random() > 0.5) IO.pure("heads") else IO.pure("tails")
104
}
105
106
// Async callback
107
val asyncIO = IO.async[String] { callback =>
108
// Simulate async operation
109
new Thread(() => {
110
Thread.sleep(1000)
111
callback(Right("Async result"))
112
}).start()
113
}
114
115
// Error handling
116
val errorIO = IO.raiseError(new RuntimeException("Something went wrong"))
117
118
// Sleep
119
val sleepIO = IO.sleep(2.seconds)
120
```
121
122
### IO Operations
123
124
Core monad operations and transformations.
125
126
```scala { .api }
127
/**
128
* Core IO operations providing monad functionality and transformations
129
*/
130
abstract class IO[+A] {
131
/** Transform the result using a function */
132
def map[B](f: A => B): IO[B]
133
134
/** Chain another IO computation */
135
def flatMap[B](f: A => IO[B]): IO[B]
136
137
/** Replace the result with a constant value */
138
def as[B](b: B): IO[B]
139
140
/** Discard the result */
141
def void: IO[Unit]
142
143
/** Apply an effect and return the original value */
144
def tap[B](f: A => IO[B]): IO[A]
145
146
/** Combine with another IO using a function */
147
def map2[B, C](that: IO[B])(f: (A, B) => C): IO[C]
148
149
/** Sequence this IO after another, discarding the other's result */
150
def *>[B](that: IO[B]): IO[B]
151
152
/** Sequence another IO after this one, discarding that one's result */
153
def <*[B](that: IO[B]): IO[A]
154
155
/** Combine with another IO in parallel */
156
def parProduct[B](that: IO[B]): IO[(A, B)]
157
158
/** Map over both results of a parallel combination */
159
def parMap2[B, C](that: IO[B])(f: (A, B) => C): IO[C]
160
}
161
```
162
163
### Error Handling
164
165
Error handling and recovery operations.
166
167
```scala { .api }
168
/**
169
* Error handling operations for managing failures in IO computations
170
*/
171
abstract class IO[+A] {
172
/** Convert errors to Either */
173
def attempt: IO[Either[Throwable, A]]
174
175
/** Handle errors by providing a recovery function */
176
def handleErrorWith[AA >: A](f: Throwable => IO[AA]): IO[AA]
177
178
/** Handle errors by providing a pure recovery value */
179
def handleError[AA >: A](f: Throwable => AA): IO[AA]
180
181
/** Recover from specific error types */
182
def recover[AA >: A](pf: PartialFunction[Throwable, AA]): IO[AA]
183
184
/** Recover from specific error types with an IO */
185
def recoverWith[AA >: A](pf: PartialFunction[Throwable, IO[AA]]): IO[AA]
186
187
/** Redeem combines map and handleErrorWith */
188
def redeem[B](recover: Throwable => B, map: A => B): IO[B]
189
190
/** RedeemWith combines flatMap and handleErrorWith */
191
def redeemWith[B](recover: Throwable => IO[B], bind: A => IO[B]): IO[B]
192
}
193
```
194
195
**Usage Examples:**
196
197
```scala
198
val program = for {
199
result <- IO.delay(riskyOperation())
200
.handleErrorWith {
201
case _: IOException => IO.pure("default value")
202
case other => IO.raiseError(other)
203
}
204
_ <- IO(println(s"Result: $result"))
205
} yield result
206
207
// Using attempt
208
val safeProgram = riskyIO.attempt.flatMap {
209
case Right(value) => IO(println(s"Success: $value"))
210
case Left(error) => IO(println(s"Error: ${error.getMessage}"))
211
}
212
```
213
214
### Resource Management
215
216
Bracket operations for safe resource acquisition and cleanup.
217
218
```scala { .api }
219
/**
220
* Resource management operations using bracket pattern
221
*/
222
abstract class IO[+A] {
223
/** Bracket pattern for resource management */
224
def bracket[B](use: A => IO[B])(release: A => IO[Unit]): IO[B]
225
226
/** Bracket with exit case information */
227
def bracketCase[B](use: A => IO[B])(release: (A, ExitCase[Throwable]) => IO[Unit]): IO[B]
228
229
/** Always execute finalizer */
230
def guarantee(finalizer: IO[Unit]): IO[A]
231
232
/** Execute finalizer with exit case information */
233
def guaranteeCase(finalizer: ExitCase[Throwable] => IO[Unit]): IO[A]
234
}
235
236
/**
237
* Exit case information for resource cleanup
238
*/
239
sealed trait ExitCase[+E]
240
object ExitCase {
241
case object Completed extends ExitCase[Nothing]
242
case class Error[E](e: E) extends ExitCase[E]
243
case object Canceled extends ExitCase[Nothing]
244
}
245
```
246
247
**Usage Examples:**
248
249
```scala
250
// File resource management
251
def withFile[A](path: String)(use: BufferedReader => IO[A]): IO[A] =
252
IO(new BufferedReader(new FileReader(path)))
253
.bracket(use)(reader => IO(reader.close()))
254
255
// Database connection with error handling
256
def withConnection[A](use: Connection => IO[A]): IO[A] =
257
acquireConnection.bracketCase(use) { (conn, exitCase) =>
258
exitCase match {
259
case ExitCase.Completed => IO(conn.commit()) *> IO(conn.close())
260
case ExitCase.Error(_) => IO(conn.rollback()) *> IO(conn.close())
261
case ExitCase.Canceled => IO(conn.close())
262
}
263
}
264
```
265
266
### Concurrency Operations
267
268
Operations for concurrent execution and fiber management.
269
270
```scala { .api }
271
/**
272
* Concurrency operations for managing concurrent execution
273
*/
274
abstract class IO[+A] {
275
/** Start this IO in a new fiber */
276
def start: IO[Fiber[IO, A]]
277
278
/** Race this IO against another */
279
def race[B](that: IO[B]): IO[Either[A, B]]
280
281
/** Race with detailed fiber information */
282
def racePair[B](that: IO[B]): IO[Either[(A, Fiber[IO, B]), (Fiber[IO, A], B)]]
283
284
/** Set a timeout for this IO */
285
def timeout(duration: FiniteDuration): IO[A]
286
287
/** Set a timeout with a fallback value */
288
def timeoutTo[AA >: A](duration: FiniteDuration, fallback: IO[AA]): IO[AA]
289
290
/** Add a delay before execution */
291
def delayBy(duration: FiniteDuration): IO[A]
292
293
/** Make this IO uncancelable */
294
def uncancelable: IO[A]
295
}
296
297
/**
298
* Fiber represents a running IO computation
299
*/
300
abstract class Fiber[F[_], A] {
301
/** Wait for the fiber to complete */
302
def join: F[A]
303
304
/** Cancel the fiber */
305
def cancel: CancelToken[F]
306
}
307
308
/** Cancellation token */
309
type CancelToken[F[_]] = F[Unit]
310
```
311
312
**Usage Examples:**
313
314
```scala
315
// Concurrent execution
316
val program = for {
317
fiber1 <- IO.delay(expensiveComputation1()).start
318
fiber2 <- IO.delay(expensiveComputation2()).start
319
result1 <- fiber1.join
320
result2 <- fiber2.join
321
} yield (result1, result2)
322
323
// Racing computations
324
val raced = IO.delay(slowOperation()).race(IO.sleep(5.seconds))
325
// Returns Either[String, Unit] - Left if operation completes, Right if timeout
326
327
// Timeout with fallback
328
val withTimeout = longRunningIO.timeoutTo(10.seconds, IO.pure("timeout"))
329
```
330
331
### Safe Execution
332
333
Safe execution methods that return IO for composability.
334
335
```scala { .api }
336
/**
337
* Safe execution methods for running IO computations
338
*/
339
abstract class IO[+A] {
340
/** Run asynchronously with a callback returning IO */
341
def runAsync(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit]
342
343
/** Run with cancellation support returning IO */
344
def runCancelable(cb: Either[Throwable, A] => IO[Unit]): SyncIO[CancelToken[IO]]
345
346
/** Convert to other effect types */
347
def to[F[_]](implicit F: LiftIO[F]): F[A]
348
}
349
350
/**
351
* Cancellation token type
352
*/
353
type CancelToken[F[_]] = F[Unit]
354
```
355
356
### Unsafe Operations
357
358
Unsafe operations for executing IO computations (should only be used at application boundaries).
359
360
```scala { .api }
361
/**
362
* Unsafe operations for executing IO - use only at application boundaries
363
*/
364
abstract class IO[+A] {
365
/** Synchronously execute this IO (blocking) */
366
def unsafeRunSync(): A
367
368
/** Asynchronously execute this IO with a callback */
369
def unsafeRunAsync(cb: Either[Throwable, A] => Unit): Unit
370
371
/** Execute with cancellation support */
372
def unsafeRunCancelable(cb: Either[Throwable, A] => Unit): CancelToken[IO]
373
374
/** Execute with a time limit */
375
def unsafeRunTimed(limit: Duration): Option[A]
376
377
/** Convert to Future */
378
def unsafeToFuture(): Future[A]
379
}
380
```
381
382
**Usage Examples:**
383
384
```scala
385
// Only use these at the very edges of your application
386
val result: String = myIO.unsafeRunSync() // Blocks until completion
387
388
myIO.unsafeRunAsync {
389
case Right(value) => println(s"Success: $value")
390
case Left(error) => println(s"Error: ${error.getMessage}")
391
}
392
393
// Convert to Future for interop
394
val future: Future[String] = myIO.unsafeToFuture()
395
```
396
397
## Types
398
399
```scala { .api }
400
/**
401
* The IO monad - represents a suspended side-effecting computation
402
*/
403
abstract class IO[+A] {
404
// Methods defined above
405
}
406
407
/**
408
* Parallel newtype for IO enabling parallel operations
409
*/
410
case class Par[+A](io: IO[A])
411
412
object IO {
413
object Par {
414
def apply[A](io: IO[A]): IO.Par[A]
415
def unwrap[A](par: IO.Par[A]): IO[A]
416
}
417
}
418
419
/**
420
* Fiber represents a running IO computation
421
*/
422
abstract class Fiber[F[_], A] {
423
/** Wait for the fiber to complete */
424
def join: F[A]
425
426
/** Cancel the fiber */
427
def cancel: CancelToken[F]
428
}
429
430
/**
431
* Synchronous IO type for safe operations
432
*/
433
abstract class SyncIO[+A] {
434
def map[B](f: A => B): SyncIO[B]
435
def flatMap[B](f: A => SyncIO[B]): SyncIO[B]
436
def unsafeRunSync(): A
437
}
438
439
/**
440
* IO trace for debugging
441
*/
442
abstract class IOTrace
443
444
/**
445
* Context shift for thread management
446
*/
447
abstract class ContextShift[F[_]] {
448
def shift: F[Unit]
449
def evalOn(ec: ExecutionContext): F[Unit]
450
}
451
452
/**
453
* Timer for time-based operations
454
*/
455
abstract class Timer[F[_]] {
456
def clock: Clock[F]
457
def sleep(duration: FiniteDuration): F[Unit]
458
}
459
```