0
# Resource Management
1
2
Cats Effect provides comprehensive resource management through the Resource data type and bracket operations, ensuring proper cleanup of resources even in the presence of errors and cancellation.
3
4
## Capabilities
5
6
### Resource Data Type
7
8
The Resource data type provides safe resource handling with automatic cleanup.
9
10
```scala { .api }
11
/**
12
* Resource management data type with automatic cleanup
13
*/
14
abstract class Resource[F[_], A] {
15
/** Use the resource safely with automatic cleanup */
16
def use[B](f: A => F[B]): F[B]
17
18
/** Get the resource and its cleanup function */
19
def allocated: F[(A, F[Unit])]
20
21
/** Transform the resource value */
22
def map[B](f: A => B): Resource[F, B]
23
24
/** Chain another resource computation */
25
def flatMap[B](f: A => Resource[F, B]): Resource[F, B]
26
27
/** Replace the resource value with a constant */
28
def as[B](b: B): Resource[F, B]
29
30
/** Discard the resource value */
31
def void: Resource[F, Unit]
32
33
/** Apply an effect and return the original resource */
34
def evalTap[B](f: A => F[B]): Resource[F, A]
35
36
/** Combine with another resource */
37
def product[B](that: Resource[F, B]): Resource[F, (A, B)]
38
39
/** Map over both resources in a product */
40
def map2[B, C](that: Resource[F, B])(f: (A, B) => C): Resource[F, C]
41
42
/** Combine resources in parallel */
43
def parProduct[B](that: Resource[F, B]): Resource[F, (A, B)]
44
45
/** Map over parallel resource combination */
46
def parMap2[B, C](that: Resource[F, B])(f: (A, B) => C): Resource[F, C]
47
}
48
```
49
50
### Resource Construction
51
52
Various ways to construct Resource instances.
53
54
```scala { .api }
55
object Resource {
56
/** Create a resource with explicit acquire and release */
57
def make[F[_], A](acquire: F[A])(release: A => F[Unit])(implicit F: Bracket[F, Throwable]): Resource[F, A]
58
59
/** Create a resource from an AutoCloseable */
60
def fromAutoCloseable[F[_], A <: AutoCloseable](fa: F[A])(implicit F: Sync[F]): Resource[F, A]
61
62
/** Create a resource from an AutoCloseable with custom close */
63
def fromAutoCloseableBlocking[F[_], A <: AutoCloseable](fa: F[A], blocker: Blocker)(implicit F: Sync[F], cs: ContextShift[F]): Resource[F, A]
64
65
/** Lift an effect into a resource (no cleanup needed) */
66
def liftF[F[_], A](fa: F[A])(implicit F: Applicative[F]): Resource[F, A]
67
68
/** Create a resource from a pure value */
69
def pure[F[_], A](a: A)(implicit F: Applicative[F]): Resource[F, A]
70
71
/** Create a resource that always fails */
72
def raiseError[F[_], A](e: Throwable)(implicit F: ApplicativeError[F, Throwable]): Resource[F, A]
73
74
/** Create an empty resource */
75
def unit[F[_]](implicit F: Applicative[F]): Resource[F, Unit]
76
77
/** Suspend resource construction */
78
def suspend[F[_], A](fa: F[Resource[F, A]])(implicit F: Sync[F]): Resource[F, A]
79
80
/** Create resource from a finalizer and value */
81
def apply[F[_], A](acquire: F[A], release: F[Unit])(implicit F: Bracket[F, Throwable]): Resource[F, A]
82
}
83
```
84
85
**Usage Examples:**
86
87
```scala
88
import cats.effect._
89
import java.io._
90
91
// File resource with automatic closing
92
def fileResource[F[_]: Sync](path: String): Resource[F, BufferedReader] =
93
Resource.fromAutoCloseable(
94
Sync[F].delay(new BufferedReader(new FileReader(path)))
95
)
96
97
// Database connection resource
98
def dbConnectionResource[F[_]: Sync]: Resource[F, Connection] =
99
Resource.make(
100
acquire = Sync[F].delay(DriverManager.getConnection(url, user, password))
101
)(
102
release = conn => Sync[F].delay(conn.close())
103
)
104
105
// HTTP client resource
106
def httpClientResource[F[_]: Sync]: Resource[F, HttpClient] =
107
Resource.make(
108
acquire = Sync[F].delay(HttpClient.newHttpClient())
109
)(
110
release = client => Sync[F].delay(client.close())
111
)
112
113
// Using resources
114
def readFileContent[F[_]: Sync](path: String): F[String] =
115
fileResource(path).use { reader =>
116
Sync[F].delay(reader.lines().collect(Collectors.joining("\n")))
117
}
118
```
119
120
### Resource Composition
121
122
Combining multiple resources safely.
123
124
```scala { .api }
125
/**
126
* Combining and sequencing resources
127
*/
128
object Resource {
129
/** Sequence a list of resources */
130
def sequence[F[_], A](resources: List[Resource[F, A]]): Resource[F, List[A]]
131
132
/** Traverse with resource construction */
133
def traverse[F[_], A, B](list: List[A])(f: A => Resource[F, B]): Resource[F, List[B]]
134
135
/** Combine two resources into a tuple */
136
def both[F[_], A, B](ra: Resource[F, A], rb: Resource[F, B]): Resource[F, (A, B)]
137
}
138
139
/** Resource instance operations */
140
abstract class Resource[F[_], A] {
141
/** Combine with another resource */
142
def zip[B](that: Resource[F, B]): Resource[F, (A, B)]
143
144
/** Combine with another resource, keeping only the first */
145
def zipLeft[B](that: Resource[F, B]): Resource[F, A]
146
147
/** Combine with another resource, keeping only the second */
148
def zipRight[B](that: Resource[F, B]): Resource[F, B]
149
}
150
```
151
152
**Usage Examples:**
153
154
```scala
155
// Multiple resource composition
156
def multiResourceExample[F[_]: Sync]: Resource[F, (Connection, HttpClient, BufferedReader)] =
157
for {
158
db <- dbConnectionResource[F]
159
client <- httpClientResource[F]
160
file <- fileResource[F]("config.txt")
161
} yield (db, client, file)
162
163
// Parallel resource acquisition
164
def parallelResources[F[_]: Concurrent]: Resource[F, (String, String)] =
165
(
166
Resource.liftF(fetchFromAPI[F]("endpoint1")),
167
Resource.liftF(fetchFromAPI[F]("endpoint2"))
168
).parMapN((a, b) => (a, b))
169
170
// Resource list processing
171
def processFiles[F[_]: Sync](paths: List[String]): F[List[String]] =
172
Resource.traverse(paths)(fileResource[F]).use { readers =>
173
readers.traverse(reader => Sync[F].delay(reader.readLine()))
174
}
175
```
176
177
### Bracket Operations
178
179
Lower-level bracket operations for custom resource management.
180
181
```scala { .api }
182
/**
183
* Bracket operations for resource management patterns
184
*/
185
trait Bracket[F[_], E] {
186
/** Basic bracket operation */
187
def bracket[A, B](acquire: F[A])(use: A => F[B])(release: A => F[Unit]): F[B]
188
189
/** Bracket with access to exit case */
190
def bracketCase[A, B](acquire: F[A])(use: A => F[B])(release: (A, ExitCase[E]) => F[Unit]): F[B]
191
192
/** Ensure a finalizer always runs */
193
def guarantee[A](fa: F[A])(finalizer: F[Unit]): F[A]
194
195
/** Guarantee with access to exit case */
196
def guaranteeCase[A](fa: F[A])(finalizer: ExitCase[E] => F[Unit]): F[A]
197
198
/** Make an effect uncancelable */
199
def uncancelable[A](fa: F[A]): F[A]
200
201
/** Ensure finalizer runs regardless of outcome */
202
def onCancel[A](fa: F[A])(finalizer: F[Unit]): F[A]
203
}
204
205
/**
206
* Exit cases for bracket operations
207
*/
208
sealed abstract class ExitCase[+E]
209
object ExitCase {
210
/** Successful completion */
211
case object Completed extends ExitCase[Nothing]
212
213
/** Failed with error */
214
case class Error[E](e: E) extends ExitCase[E]
215
216
/** Canceled before completion */
217
case object Canceled extends ExitCase[Nothing]
218
}
219
```
220
221
**Usage Examples:**
222
223
```scala
224
// Custom resource with bracket
225
def withCustomResource[F[_]: Bracket[*[_], Throwable], A](
226
use: CustomResource => F[A]
227
): F[A] =
228
Bracket[F, Throwable].bracket(
229
acquire = Sync[F].delay {
230
val resource = new CustomResource()
231
resource.initialize()
232
resource
233
}
234
)(
235
use = use
236
)(
237
release = resource => Sync[F].delay(resource.cleanup())
238
)
239
240
// Bracket with exit case handling
241
def bracketWithLogging[F[_]: Bracket[*[_], Throwable], A](
242
acquire: F[A]
243
)(use: A => F[Unit]): F[Unit] =
244
Bracket[F, Throwable].bracketCase(acquire)(use) { (resource, exitCase) =>
245
val logMessage = exitCase match {
246
case ExitCase.Completed => "Resource used successfully"
247
case ExitCase.Error(e) => s"Resource failed with: ${e.getMessage}"
248
case ExitCase.Canceled => "Resource usage was canceled"
249
}
250
Sync[F].delay(println(logMessage))
251
}
252
```
253
254
### Resource Pooling
255
256
Advanced patterns for resource pooling and lifecycle management.
257
258
```scala { .api }
259
/**
260
* Resource pooling utilities (typically custom implementation)
261
*/
262
trait ResourcePool[F[_], A] {
263
/** Take a resource from the pool */
264
def take: F[A]
265
266
/** Return a resource to the pool */
267
def put(resource: A): F[Unit]
268
269
/** Use a resource from the pool temporarily */
270
def use[B](f: A => F[B]): F[B]
271
272
/** Close the entire pool */
273
def close: F[Unit]
274
}
275
276
object ResourcePool {
277
/** Create a resource pool */
278
def create[F[_]: Concurrent, A](
279
size: Int,
280
createResource: F[A],
281
destroyResource: A => F[Unit]
282
): Resource[F, ResourcePool[F, A]]
283
}
284
```
285
286
**Usage Examples:**
287
288
```scala
289
// Connection pool implementation
290
def connectionPool[F[_]: Concurrent](
291
maxConnections: Int
292
): Resource[F, ResourcePool[F, Connection]] = {
293
294
def createConnection: F[Connection] =
295
Sync[F].delay(DriverManager.getConnection(url, user, password))
296
297
def closeConnection(conn: Connection): F[Unit] =
298
Sync[F].delay(conn.close())
299
300
ResourcePool.create(maxConnections, createConnection, closeConnection)
301
}
302
303
// Using the pool
304
def databaseOperation[F[_]: Concurrent]: F[ResultSet] =
305
connectionPool[F](10).use { pool =>
306
pool.use { connection =>
307
Sync[F].delay {
308
val stmt = connection.createStatement()
309
stmt.executeQuery("SELECT * FROM users")
310
}
311
}
312
}
313
```
314
315
## Types
316
317
```scala { .api }
318
/**
319
* Resource data type for safe resource management
320
*/
321
abstract class Resource[F[_], A]
322
323
/**
324
* Type class for bracket-based resource management
325
*/
326
trait Bracket[F[_], E] extends MonadError[F, E]
327
328
/**
329
* Exit case information for bracket operations
330
*/
331
sealed abstract class ExitCase[+E]
332
333
/**
334
* Resource pool abstraction
335
*/
336
trait ResourcePool[F[_], A]
337
338
/**
339
* Type aliases for common bracket patterns
340
*/
341
type BracketThrow[F[_]] = Bracket[F, Throwable]
342
```