or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdindex.mdparallel-processing.mdracing.mdresource-management.mdsynchronization-flow.md

resource-management.mddocs/

0

# Resource Management

1

2

Arrow FX Coroutines provides comprehensive resource management capabilities that ensure safe acquisition and cleanup of resources. All resource operations integrate with structured concurrency and guarantee cleanup even in the presence of exceptions or cancellation.

3

4

## Core Resource Types

5

6

### Resource Type

7

8

```kotlin { .api }

9

typealias Resource<A> = suspend ResourceScope.() -> A

10

```

11

12

A `Resource<A>` represents a resource that can be safely acquired and will be automatically cleaned up.

13

14

### ResourceScope Interface

15

16

```kotlin { .api }

17

interface ResourceScope : AutoCloseScope {

18

suspend fun <A> Resource<A>.bind(): A

19

suspend fun <A> install(acquire: suspend AcquireStep.() -> A, release: suspend (A, ExitCase) -> Unit): A

20

suspend infix fun <A> Resource<A>.release(release: suspend (A) -> Unit): A

21

suspend infix fun <A> Resource<A>.releaseCase(release: suspend (A, ExitCase) -> Unit): A

22

fun onClose(release: (Throwable?) -> Unit): Unit

23

infix fun onRelease(release: suspend (ExitCase) -> Unit)

24

}

25

26

interface AcquireStep

27

```

28

29

## Resource Creation

30

31

### Basic Resource Creation

32

33

```kotlin { .api }

34

fun <A> resource(block: suspend ResourceScope.() -> A): Resource<A>

35

fun <A> resource(acquire: suspend () -> A, release: suspend (A, ExitCase) -> Unit): Resource<A>

36

```

37

38

Create a resource with custom acquisition and release logic.

39

40

```kotlin

41

val fileResource = resource(

42

acquire = { File("data.txt").also { it.createNewFile() } },

43

release = { file -> file.delete() }

44

)

45

```

46

47

### Resource with Exit Case Handling

48

49

```kotlin { .api }

50

fun <A> resource(acquire: suspend () -> A, release: suspend (A, ExitCase) -> Unit): Resource<A>

51

```

52

53

Create a resource that receives information about how the scope exited.

54

55

```kotlin

56

val connectionResource = resource(

57

acquire = { openDatabaseConnection() },

58

release = { connection, exitCase ->

59

when (exitCase) {

60

is ExitCase.Completed -> connection.commit()

61

is ExitCase.Cancelled -> connection.rollback()

62

is ExitCase.Failure -> connection.rollback()

63

}

64

connection.close()

65

}

66

)

67

```

68

69

### AutoCloseable Resources

70

71

```kotlin { .api }

72

fun <A : AutoCloseable> autoCloseable(closingDispatcher: CoroutineDispatcher = IODispatcher, autoCloseable: suspend () -> A): Resource<A>

73

suspend fun <A : AutoCloseable> ResourceScope.autoCloseable(closingDispatcher: CoroutineDispatcher = IODispatcher, autoCloseable: suspend () -> A): A

74

```

75

76

Create resources from objects that implement `AutoCloseable`.

77

78

```kotlin

79

val inputStreamResource = autoCloseable { FileInputStream("input.txt") }

80

val suspendingResource = autoCloseable { openAsyncConnection() }

81

```

82

83

## Resource Usage

84

85

### ResourceScope Execution

86

87

```kotlin { .api }

88

suspend fun <A> resourceScope(block: suspend ResourceScope.() -> A): A

89

```

90

91

Execute code within a resource scope where resources can be safely acquired and will be automatically cleaned up.

92

93

```kotlin

94

val result = resourceScope {

95

val file = fileResource.bind()

96

val connection = connectionResource.bind()

97

98

// Use resources safely

99

processData(file, connection)

100

// Resources automatically cleaned up here

101

}

102

```

103

104

### Use Pattern

105

106

```kotlin { .api }

107

suspend fun <A, B> Resource<A>.use(f: suspend (A) -> B): B

108

```

109

110

The use pattern for single resources.

111

112

```kotlin

113

val result = fileResource.use { file ->

114

file.readText()

115

}

116

```

117

118

### Manual Resource Allocation (Delicate API)

119

120

```kotlin { .api }

121

@DelicateCoroutinesApi

122

suspend fun <A> Resource<A>.allocate(): Pair<A, suspend (ExitCase) -> Unit>

123

```

124

125

Manually allocate a resource, returning the resource and its release function.

126

127

```kotlin

128

val (connection, release) = connectionResource.allocate()

129

try {

130

// Use connection

131

connection.query("SELECT * FROM users")

132

} finally {

133

release()

134

}

135

```

136

137

## Advanced Resource Operations

138

139

### Resource Binding

140

141

```kotlin { .api }

142

suspend fun <A> Resource<A>.bind(): A

143

```

144

145

Bind a resource to the current scope, ensuring it will be cleaned up when the scope exits.

146

147

```kotlin

148

resourceScope {

149

val file = fileResource.bind()

150

val stream = streamResource.bind()

151

152

// Both resources cleaned up automatically

153

processFileWithStream(file, stream)

154

}

155

```

156

157

### Resource Installation

158

159

```kotlin { .api }

160

suspend fun <A> install(acquire: suspend () -> A, release: suspend (A) -> Unit): A

161

```

162

163

Install a resource directly in the current scope.

164

165

```kotlin

166

resourceScope {

167

val tempFile = install(

168

acquire = { createTempFile() },

169

release = { it.delete() }

170

)

171

172

processFile(tempFile)

173

}

174

```

175

176

### Adding Release Actions

177

178

```kotlin { .api }

179

infix fun <A> Resource<A>.release(release: suspend (A) -> Unit): Resource<A>

180

infix fun <A> Resource<A>.releaseCase(release: suspend (A, ExitCase) -> Unit): Resource<A>

181

```

182

183

Add additional release actions to existing resources.

184

185

```kotlin

186

val enhancedResource = fileResource

187

.release { file -> file.setReadOnly() }

188

.releaseCase { file, exitCase ->

189

if (exitCase is ExitCase.Failure) {

190

logError("Resource failed", exitCase.failure)

191

}

192

}

193

```

194

195

### Scope-Level Release Handlers

196

197

```kotlin { .api }

198

infix fun onRelease(release: suspend () -> Unit)

199

```

200

201

Register a release handler that will run when the scope exits.

202

203

```kotlin

204

resourceScope {

205

onRelease { println("Cleaning up scope") }

206

207

val file = fileResource.bind()

208

processFile(file)

209

// Scope cleanup message printed after file cleanup

210

}

211

```

212

213

## Flow Integration

214

215

### Resource as Flow

216

217

```kotlin { .api }

218

fun <A> Resource<A>.asFlow(): Flow<A>

219

```

220

221

Convert a resource to a Flow that manages the resource lifecycle.

222

223

```kotlin

224

val dataFlow = fileResource.asFlow().map { file ->

225

file.readLines()

226

}.flatten()

227

```

228

229

## Exit Cases

230

231

### ExitCase Sealed Class

232

233

```kotlin { .api }

234

sealed class ExitCase {

235

object Completed : ExitCase()

236

data class Cancelled(val exception: CancellationException) : ExitCase()

237

data class Failure(val failure: Throwable) : ExitCase()

238

239

companion object {

240

fun ExitCase(error: Throwable): ExitCase

241

}

242

}

243

```

244

245

Exit cases provide information about how a resource scope or operation terminated:

246

247

- `ExitCase.Completed`: The operation completed successfully

248

- `ExitCase.Cancelled`: The operation was cancelled

249

- `ExitCase.Failure`: The operation failed with an exception

250

251

## Resource Composition Examples

252

253

### Sequential Resource Dependencies

254

255

```kotlin

256

val appResource = resource(

257

acquire = {

258

resourceScope {

259

val config = configResource.bind()

260

val database = databaseResource(config).bind()

261

val cache = cacheResource(database).bind()

262

263

Application(database, cache)

264

}

265

},

266

release = { app -> app.shutdown() }

267

)

268

```

269

270

### Parallel Resource Initialization

271

272

```kotlin

273

val parallelResources = resource(

274

acquire = {

275

parZip(

276

{ serviceAResource.use { it } },

277

{ serviceBResource.use { it } },

278

{ serviceCResource.use { it } }

279

) { serviceA, serviceB, serviceC ->

280

CombinedServices(serviceA, serviceB, serviceC)

281

}

282

},

283

release = { services -> services.shutdown() }

284

)

285

```

286

287

### Resource Pool Management

288

289

```kotlin

290

class ConnectionPool(private val maxConnections: Int) {

291

fun getConnection(): Resource<Connection> = resource(

292

acquire = { acquireFromPool() },

293

release = { connection -> returnToPool(connection) }

294

)

295

296

private suspend fun acquireFromPool(): Connection = TODO()

297

private suspend fun returnToPool(connection: Connection) = TODO()

298

}

299

```

300

301

## Bracket Pattern Functions

302

303

The bracket pattern provides fundamental resource safety guarantees without the overhead of the full Resource system. These functions ensure cleanup even when exceptions or cancellation occur.

304

305

### Basic Bracket

306

307

```kotlin { .api }

308

suspend fun <A, B> bracket(

309

crossinline acquire: suspend () -> A,

310

use: suspend (A) -> B,

311

crossinline release: suspend (A) -> Unit

312

): B

313

```

314

315

Acquire a resource, use it, and guarantee cleanup regardless of how the operation exits.

316

317

```kotlin

318

val result = bracket(

319

acquire = { openFile("data.txt") },

320

use = { file -> file.readText() },

321

release = { file -> file.close() }

322

)

323

```

324

325

### Bracket with Exit Case

326

327

```kotlin { .api }

328

suspend fun <A, B> bracketCase(

329

crossinline acquire: suspend () -> A,

330

use: suspend (A) -> B,

331

crossinline release: suspend (A, ExitCase) -> Unit

332

): B

333

```

334

335

Like bracket, but the release function receives information about how the operation exited.

336

337

```kotlin

338

val result = bracketCase(

339

acquire = { openDatabaseTransaction() },

340

use = { tx -> tx.performOperations() },

341

release = { tx, exitCase ->

342

when (exitCase) {

343

is ExitCase.Completed -> tx.commit()

344

is ExitCase.Cancelled, is ExitCase.Failure -> tx.rollback()

345

}

346

}

347

)

348

```

349

350

### Guarantee Finalizer

351

352

```kotlin { .api }

353

suspend fun <A> guarantee(

354

fa: suspend () -> A,

355

crossinline finalizer: suspend () -> Unit

356

): A

357

```

358

359

Execute an operation and guarantee a finalizer runs afterwards, regardless of how the operation exits.

360

361

```kotlin

362

val result = guarantee(

363

fa = { performOperation() },

364

finalizer = { cleanup() }

365

)

366

```

367

368

### Guarantee with Exit Case

369

370

```kotlin { .api }

371

suspend fun <A> guaranteeCase(

372

fa: suspend () -> A,

373

crossinline finalizer: suspend (ExitCase) -> Unit

374

): A

375

```

376

377

Like guarantee, but the finalizer receives information about how the operation exited.

378

379

```kotlin

380

val result = guaranteeCase(

381

fa = { riskyOperation() },

382

finalizer = { exitCase ->

383

when (exitCase) {

384

is ExitCase.Completed -> logSuccess()

385

is ExitCase.Cancelled -> logCancellation()

386

is ExitCase.Failure -> logError(exitCase.failure)

387

}

388

}

389

)

390

```

391

392

### Cancellation Handler

393

394

```kotlin { .api }

395

suspend fun <A> onCancel(

396

fa: suspend () -> A,

397

crossinline onCancel: suspend () -> Unit

398

): A

399

```

400

401

Execute an operation and run a specific handler only if the operation is cancelled.

402

403

```kotlin

404

val result = onCancel(

405

fa = { longRunningOperation() },

406

onCancel = { notifyCancellation() }

407

)

408

```