0
# Effect Type Classes
1
2
Cats Effect provides a hierarchy of type classes that define capabilities for synchronous, asynchronous, and concurrent effects. This enables abstraction over different effect types while maintaining type safety and composability.
3
4
## Capabilities
5
6
### Bracket Type Class
7
8
Base type class for resource management and safe cleanup operations.
9
10
```scala { .api }
11
/**
12
* Type class for resource management with bracket pattern
13
*/
14
trait Bracket[F[_], E] extends MonadError[F, E] {
15
/** Bracket with detailed exit case information */
16
def bracketCase[A, B](acquire: F[A])(use: A => F[B])(release: (A, ExitCase[E]) => F[Unit]): F[B]
17
18
/** Simple bracket pattern (implemented) */
19
def bracket[A, B](acquire: F[A])(use: A => F[B])(release: A => F[Unit]): F[B] =
20
bracketCase(acquire)(use)((a, _) => release(a))
21
22
/** Make an effect uncancelable (implemented) */
23
def uncancelable[A](fa: F[A]): F[A] =
24
bracket(fa)(pure)(_ => unit)
25
26
/** Always execute finalizer (implemented) */
27
def guarantee[A](fa: F[A])(finalizer: F[Unit]): F[A] =
28
bracket(unit)(_ => fa)(_ => finalizer)
29
30
/** Execute finalizer with exit case (implemented) */
31
def guaranteeCase[A](fa: F[A])(finalizer: ExitCase[E] => F[Unit]): F[A] =
32
bracketCase(unit)(_ => fa)((_, e) => finalizer(e))
33
34
/** Execute finalizer only on cancellation (implemented) */
35
def onCancel[A](fa: F[A])(finalizer: F[Unit]): F[A] =
36
guaranteeCase(fa) {
37
case ExitCase.Canceled => finalizer
38
case _ => unit
39
}
40
}
41
42
/**
43
* Exit case information for resource cleanup
44
*/
45
sealed abstract class ExitCase[+E] extends Product with Serializable
46
47
object ExitCase {
48
case object Completed extends ExitCase[Nothing]
49
final case class Error[+E](e: E) extends ExitCase[E]
50
case object Canceled extends ExitCase[Nothing]
51
}
52
```
53
54
### Sync Type Class
55
56
Base type class for synchronous effects that can suspend side-effecting computations.
57
58
```scala { .api }
59
/**
60
* Type class for synchronous effects with suspension capabilities
61
*/
62
trait Sync[F[_]] extends BracketThrow[F] with Defer[F] {
63
/** Suspend an F computation for stack safety (deprecated, use defer) */
64
def suspend[A](fa: => F[A]): F[A]
65
66
/** Suspend an F computation for stack safety (preferred over suspend) */
67
def defer[A](fa: => F[A]): F[A]
68
69
/** Suspend a side-effecting computation */
70
def delay[A](thunk: => A): F[A]
71
}
72
73
/** Type alias for Bracket[F, Throwable] */
74
type BracketThrow[F[_]] = Bracket[F, Throwable]
75
76
object Sync {
77
def apply[F[_]](implicit F: Sync[F]): Sync[F] = F
78
79
/** Syntax for delay operations */
80
implicit class SyncOps[F[_], A](fa: F[A])(implicit F: Sync[F]) {
81
def delayBy(duration: FiniteDuration): F[A]
82
}
83
}
84
```
85
86
**Usage Examples:**
87
88
```scala
89
def readFile[F[_]: Sync](path: String): F[String] =
90
Sync[F].delay {
91
scala.io.Source.fromFile(path).mkString
92
}
93
94
def safeComputation[F[_]: Sync](input: Int): F[Int] =
95
Sync[F].defer {
96
if (input < 0) Sync[F].raiseError(new IllegalArgumentException("Negative input"))
97
else Sync[F].delay(input * 2)
98
}
99
```
100
101
### Async Type Class
102
103
Type class for asynchronous effects that can handle callback-based operations.
104
105
```scala { .api }
106
/**
107
* Type class for asynchronous effects with callback support
108
*/
109
trait Async[F[_]] extends Sync[F] with LiftIO[F] {
110
/** Create an async effect from a callback */
111
def async[A](k: (Either[Throwable, A] => Unit) => Unit): F[A]
112
113
/** Create an async effect with effectful registration */
114
def asyncF[A](k: (Either[Throwable, A] => Unit) => F[Unit]): F[A]
115
116
/** An effect that never completes (implemented via async) */
117
def never[A]: F[A] = async(_ => ())
118
}
119
120
object Async {
121
def apply[F[_]](implicit F: Async[F]): Async[F] = F
122
123
/** Shift execution to a different ExecutionContext */
124
def shift[F[_]](ec: ExecutionContext)(implicit F: Async[F]): F[Unit]
125
126
/** Convert a Future to F */
127
def fromFuture[F[_], A](fa: F[Future[A]])(implicit F: Async[F]): F[A]
128
129
/** Memoize an effect */
130
def memoize[F[_], A](f: F[A])(implicit F: Async[F]): F[F[A]]
131
}
132
```
133
134
**Usage Examples:**
135
136
```scala
137
def httpRequest[F[_]: Async](url: String): F[String] =
138
Async[F].async { callback =>
139
// Simulate async HTTP request
140
val client = new AsyncHttpClient()
141
client.get(url) { response =>
142
callback(Right(response.body))
143
}
144
}
145
146
def delayedComputation[F[_]: Async](duration: FiniteDuration): F[Unit] =
147
Async[F].async { callback =>
148
val timer = new Timer()
149
timer.schedule(new TimerTask {
150
def run(): Unit = callback(Right(()))
151
}, duration.toMillis)
152
}
153
```
154
155
### Concurrent Type Class
156
157
Type class for concurrent effects with fiber support and cancellation.
158
159
```scala { .api }
160
/**
161
* Type class for concurrent effects with fiber support
162
*/
163
trait Concurrent[F[_]] extends Async[F] {
164
/** Start a fiber running the given effect */
165
def start[A](fa: F[A]): F[Fiber[F, A]]
166
167
/** Race two effects, returning winner and loser fiber */
168
def racePair[A, B](fa: F[A], fb: F[B]): F[Either[(A, Fiber[F, B]), (Fiber[F, A], B)]]
169
170
/** Race two effects, returning the first to complete (implemented) */
171
def race[A, B](fa: F[A], fb: F[B]): F[Either[A, B]] =
172
flatMap(racePair(fa, fb)) {
173
case Left((a, fiberB)) => map(fiberB.cancel)(_ => Left(a))
174
case Right((fiberA, b)) => map(fiberA.cancel)(_ => Right(b))
175
}
176
177
/** Create a cancelable effect (implemented) */
178
def cancelable[A](k: (Either[Throwable, A] => Unit) => CancelToken[F]): F[A]
179
180
/** Create background resource (implemented) */
181
def background[A](fa: F[A]): Resource[F, F[A]] =
182
Resource.make(start(fa))(_.cancel).map(_.join)
183
}
184
185
object Concurrent {
186
def apply[F[_]](implicit F: Concurrent[F]): Concurrent[F] = F
187
188
/** Timeout an effect */
189
def timeout[F[_], A](fa: F[A], duration: FiniteDuration)(implicit F: Concurrent[F], timer: Timer[F]): F[A]
190
191
/** Timeout with fallback */
192
def timeoutTo[F[_], A](fa: F[A], duration: FiniteDuration, fallback: F[A])(implicit F: Concurrent[F], timer: Timer[F]): F[A]
193
}
194
```
195
196
**Usage Examples:**
197
198
```scala
199
def parallelProcessing[F[_]: Concurrent](items: List[Item]): F[List[Result]] = {
200
val fibers = items.traverse(item => processItem(item).start)
201
fibers.flatMap(_.traverse(_.join))
202
}
203
204
def raceWithTimeout[F[_]: Concurrent: Timer, A](fa: F[A], timeout: FiniteDuration): F[Either[A, Unit]] =
205
Concurrent[F].race(fa, Timer[F].sleep(timeout))
206
```
207
208
### Bracket Type Class
209
210
Type class for resource management with automatic cleanup.
211
212
```scala { .api }
213
/**
214
* Type class for resource management with bracket operations
215
*/
216
trait Bracket[F[_], E] extends MonadError[F, E] {
217
/** Bracket resource management pattern */
218
def bracket[A, B](acquire: F[A])(use: A => F[B])(release: A => F[Unit]): F[B]
219
220
/** Bracket with access to exit case */
221
def bracketCase[A, B](acquire: F[A])(use: A => F[B])(release: (A, ExitCase[E]) => F[Unit]): F[B]
222
223
/** Ensure a finalizer always runs */
224
def guarantee[A](fa: F[A])(finalizer: F[Unit]): F[A]
225
226
/** Guarantee with access to exit case */
227
def guaranteeCase[A](fa: F[A])(finalizer: ExitCase[E] => F[Unit]): F[A]
228
229
/** Make an effect uncancelable */
230
def uncancelable[A](fa: F[A]): F[A]
231
}
232
233
/**
234
* Exit case for bracket operations
235
*/
236
sealed abstract class ExitCase[+E]
237
object ExitCase {
238
case object Completed extends ExitCase[Nothing]
239
case class Error[E](e: E) extends ExitCase[E]
240
case object Canceled extends ExitCase[Nothing]
241
}
242
```
243
244
**Usage Examples:**
245
246
```scala
247
def withResource[F[_]: Bracket[*[_], Throwable], A, B](
248
acquire: F[A]
249
)(use: A => F[B])(release: A => F[Unit]): F[B] =
250
Bracket[F, Throwable].bracket(acquire)(use)(release)
251
252
def safeFileOperation[F[_]: Bracket[*[_], Throwable]](path: String): F[String] =
253
Bracket[F, Throwable].bracket(
254
acquire = Sync[F].delay(new FileInputStream(path))
255
)(
256
use = stream => Sync[F].delay(/* read from stream */)
257
)(
258
release = stream => Sync[F].delay(stream.close())
259
)
260
```
261
262
### Effect Execution Type Classes
263
264
Type classes for executing effects in different contexts.
265
266
```scala { .api }
267
/**
268
* Type class for effects that can be executed asynchronously
269
*/
270
trait Effect[F[_]] extends Async[F] {
271
def runAsync[A](fa: F[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit]
272
}
273
274
/**
275
* Type class for effects that can be executed synchronously
276
*/
277
trait SyncEffect[F[_]] extends Effect[F] {
278
def runSync[A](fa: F[A]): A
279
}
280
281
/**
282
* Type class for concurrent effects that can be executed with cancellation
283
*/
284
trait ConcurrentEffect[F[_]] extends Concurrent[F] with Effect[F] {
285
def runCancelable[A](fa: F[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[CancelToken[SyncIO]]
286
}
287
288
/**
289
* Type class for lifting IO into other effect types
290
*/
291
trait LiftIO[F[_]] {
292
def liftIO[A](ioa: IO[A]): F[A]
293
}
294
```
295
296
### Utility Type Classes
297
298
Additional type classes for timing and context management.
299
300
```scala { .api }
301
/**
302
* Type class for time-based operations
303
*/
304
trait Timer[F[_]] {
305
def clock: Clock[F]
306
def sleep(duration: FiniteDuration): F[Unit]
307
}
308
309
/**
310
* Type class for accessing time
311
*/
312
trait Clock[F[_]] {
313
def realTime(unit: TimeUnit): F[Long]
314
def monotonic(unit: TimeUnit): F[Long]
315
}
316
317
/**
318
* Type class for managing execution context
319
*/
320
trait ContextShift[F[_]] {
321
def shift: F[Unit]
322
def evalOn[A](ec: ExecutionContext)(fa: F[A]): F[A]
323
}
324
```
325
326
## Types
327
328
```scala { .api }
329
/**
330
* Type aliases for common bracket patterns
331
*/
332
type BracketThrow[F[_]] = Bracket[F, Throwable]
333
type ApplicativeThrow[F[_]] = ApplicativeError[F, Throwable]
334
type MonadThrow[F[_]] = MonadError[F, Throwable]
335
336
/**
337
* Cancellation token type
338
*/
339
type CancelToken[F[_]] = F[Unit]
340
341
/**
342
* Fiber handle for concurrent computations
343
*/
344
abstract class Fiber[F[_], A] {
345
def join: F[A]
346
def cancel: CancelToken[F]
347
}
348
```