or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

backported-collections.mdcollection-extensions.mdindex.mdjava-conversion.mdresource-management.mdstring-parsing.mdutility-features.md

resource-management.mddocs/

0

# Resource Management

1

2

Automatic resource management with try-with-resources semantics through the Using utility. Provides safe resource handling that ensures proper cleanup even in the presence of exceptions.

3

4

## Capabilities

5

6

### Single Resource Management

7

8

Manage a single resource with automatic cleanup through the Releasable typeclass.

9

10

```scala { .api }

11

object Using {

12

/**

13

* Use a resource in a Try context with automatic cleanup

14

* @param resource Resource to manage (call-by-name for lazy evaluation)

15

* @param f Operation to perform on the resource

16

* @param releasable Typeclass instance for resource cleanup

17

* @return Try containing the result or any exception

18

*/

19

def apply[R: Releasable, A](resource: => R)(f: R => A): Try[A]

20

21

/**

22

* Use a resource with automatic cleanup, throwing exceptions directly

23

* @param resource Resource to manage

24

* @param body Operation to perform on the resource

25

* @param releasable Typeclass instance for resource cleanup

26

* @return Result of the operation

27

* @throws Exception if resource creation or operation fails

28

*/

29

def resource[R, A](resource: R)(body: R => A)(implicit releasable: Releasable[R]): A

30

}

31

```

32

33

**Usage Examples:**

34

35

```scala

36

import scala.util.Using

37

import java.io.{FileInputStream, BufferedReader, FileReader}

38

39

// Using with Try (exception handling)

40

val content = Using(new FileInputStream("data.txt")) { fis =>

41

val bytes = new Array[Byte](fis.available())

42

fis.read(bytes)

43

new String(bytes)

44

}

45

46

content match {

47

case Success(data) => println(s"File content: $data")

48

case Failure(exception) => println(s"Error reading file: $exception")

49

}

50

51

// Using with direct exceptions

52

try {

53

val result = Using.resource(new BufferedReader(new FileReader("config.txt"))) { reader =>

54

reader.lines().toArray.mkString("\n")

55

}

56

println(result)

57

} catch {

58

case ex: Exception => println(s"Failed to read config: $ex")

59

}

60

61

// Combining with other operations

62

val processedData = Using(new FileInputStream("input.dat")) { inputStream =>

63

val data = readBinaryData(inputStream)

64

processData(data)

65

transformData(data)

66

}.getOrElse(defaultData)

67

```

68

69

### Multiple Resource Management

70

71

Manage multiple resources simultaneously with guaranteed cleanup of all resources.

72

73

```scala { .api }

74

/**

75

* Manage two resources with automatic cleanup

76

* @param resource1 First resource to manage

77

* @param resource2 Second resource to manage (call-by-name)

78

* @param body Operation to perform on both resources

79

* @return Result of the operation

80

* @throws Exception if any resource creation or operation fails

81

*/

82

def resources[R1: Releasable, R2: Releasable, A](

83

resource1: R1,

84

resource2: => R2

85

)(body: (R1, R2) => A): A

86

87

/**

88

* Manage three resources with automatic cleanup

89

* @param resource1 First resource to manage

90

* @param resource2 Second resource to manage (call-by-name)

91

* @param resource3 Third resource to manage (call-by-name)

92

* @param body Operation to perform on all resources

93

* @return Result of the operation

94

*/

95

def resources[R1: Releasable, R2: Releasable, R3: Releasable, A](

96

resource1: R1,

97

resource2: => R2,

98

resource3: => R3

99

)(body: (R1, R2, R3) => A): A

100

101

/**

102

* Manage four resources with automatic cleanup

103

* @param resource1 First resource to manage

104

* @param resource2 Second resource to manage (call-by-name)

105

* @param resource3 Third resource to manage (call-by-name)

106

* @param resource4 Fourth resource to manage (call-by-name)

107

* @param body Operation to perform on all resources

108

* @return Result of the operation

109

*/

110

def resources[R1: Releasable, R2: Releasable, R3: Releasable, R4: Releasable, A](

111

resource1: R1,

112

resource2: => R2,

113

resource3: => R3,

114

resource4: => R4

115

)(body: (R1, R2, R3, R4) => A): A

116

```

117

118

**Usage Examples:**

119

120

```scala

121

import scala.util.Using

122

import java.io.{FileInputStream, FileOutputStream, BufferedReader, FileReader}

123

124

// Copy file with automatic cleanup of both streams

125

Using.resources(

126

new FileInputStream("source.txt"),

127

new FileOutputStream("destination.txt")

128

) { (input, output) =>

129

val buffer = new Array[Byte](8192)

130

var bytesRead = 0

131

while ({ bytesRead = input.read(buffer); bytesRead != -1 }) {

132

output.write(buffer, 0, bytesRead)

133

}

134

}

135

136

// Process multiple files

137

Using.resources(

138

new BufferedReader(new FileReader("config.properties")),

139

new BufferedReader(new FileReader("data.csv")),

140

new FileOutputStream("report.txt")

141

) { (configReader, dataReader, reportOutput) =>

142

val config = loadProperties(configReader)

143

val data = parseCSV(dataReader)

144

val report = generateReport(config, data)

145

reportOutput.write(report.getBytes)

146

}

147

148

// Database operations with multiple connections

149

Using.resources(

150

createReadConnection(),

151

createWriteConnection()

152

) { (readConn, writeConn) =>

153

val data = fetchData(readConn)

154

val processed = processData(data)

155

saveResults(writeConn, processed)

156

}

157

```

158

159

### Resource Manager

160

161

Advanced resource management with a manager that can handle an arbitrary number of resources.

162

163

```scala { .api }

164

final class Manager {

165

/**

166

* Register and return a resource for management

167

* @param resource Resource to manage

168

* @param releasable Typeclass instance for resource cleanup

169

* @return The resource itself

170

*/

171

def apply[R: Releasable](resource: R): R

172

173

/**

174

* Register a resource for management without returning it

175

* @param resource Resource to manage

176

* @param releasable Typeclass instance for resource cleanup

177

*/

178

def acquire[R: Releasable](resource: R): Unit

179

}

180

181

object Manager {

182

/**

183

* Execute operation with resource manager

184

* @param op Operation that uses the manager to acquire resources

185

* @return Try containing the result or any exception

186

*/

187

def apply[A](op: Manager => A): Try[A]

188

}

189

```

190

191

**Usage Examples:**

192

193

```scala

194

import scala.util.Using

195

196

// Managing multiple resources dynamically

197

val result = Using.Manager { manager =>

198

val file1 = manager(new FileInputStream("file1.txt"))

199

val file2 = manager(new FileInputStream("file2.txt"))

200

val output = manager(new FileOutputStream("merged.txt"))

201

202

// All resources are automatically managed

203

mergeFiles(file1, file2, output)

204

}

205

206

// Conditional resource acquisition

207

Using.Manager { manager =>

208

val primaryDB = manager(connectToPrimaryDatabase())

209

210

val backupDB = if (primaryDB.isHealthy) {

211

None

212

} else {

213

Some(manager(connectToBackupDatabase()))

214

}

215

216

performDatabaseOperations(primaryDB, backupDB)

217

}

218

219

// Resource acquisition in loops

220

Using.Manager { manager =>

221

val outputFiles = (1 to 10).map { i =>

222

manager(new FileOutputStream(s"output_$i.txt"))

223

}

224

225

// Process data and write to all files

226

processDataToMultipleFiles(inputData, outputFiles)

227

}

228

```

229

230

### Releasable Typeclass

231

232

The Releasable typeclass defines how resources should be cleaned up.

233

234

```scala { .api }

235

trait Releasable[-R] {

236

/**

237

* Release the resource, cleaning up any held resources

238

* @param resource Resource to release

239

*/

240

def release(resource: R): Unit

241

}

242

243

object Releasable {

244

/**

245

* Implicit instance for AutoCloseable resources

246

*/

247

implicit object AutoCloseableIsReleasable extends Releasable[AutoCloseable] {

248

def release(resource: AutoCloseable): Unit = resource.close()

249

}

250

251

/**

252

* Implicit instance for scala.io.Source (Scala 2.11 only)

253

*/

254

implicit object SourceReleasable extends Releasable[Source] {

255

def release(resource: Source): Unit = resource.close()

256

}

257

}

258

```

259

260

**Custom Releasable Instances:**

261

262

```scala

263

import scala.util.Using.Releasable

264

265

// Custom resource type

266

case class DatabaseConnection(url: String) {

267

def close(): Unit = {

268

// Close database connection

269

println(s"Closing connection to $url")

270

}

271

}

272

273

// Custom Releasable instance

274

implicit val dbReleasable: Releasable[DatabaseConnection] = new Releasable[DatabaseConnection] {

275

def release(resource: DatabaseConnection): Unit = resource.close()

276

}

277

278

// Usage with custom resource

279

Using(DatabaseConnection("jdbc:mysql://localhost/db")) { conn =>

280

// Perform database operations

281

executeQuery(conn, "SELECT * FROM users")

282

}

283

284

// Thread pool management

285

case class ThreadPool(size: Int) {

286

def shutdown(): Unit = {

287

println(s"Shutting down thread pool of size $size")

288

}

289

}

290

291

implicit val threadPoolReleasable: Releasable[ThreadPool] =

292

(resource: ThreadPool) => resource.shutdown()

293

294

Using(ThreadPool(10)) { pool =>

295

// Execute tasks using thread pool

296

executeTasks(pool, tasks)

297

}

298

```

299

300

### Exception Handling Patterns

301

302

**Graceful Degradation:**

303

304

```scala

305

import scala.util.Using

306

307

def readConfigWithFallback(primaryPath: String, fallbackPath: String): Config = {

308

Using(new FileInputStream(primaryPath)) { input =>

309

parseConfig(input)

310

}.recoverWith { case _ =>

311

Using(new FileInputStream(fallbackPath)) { input =>

312

parseConfig(input)

313

}

314

}.getOrElse(defaultConfig)

315

}

316

```

317

318

**Resource Validation:**

319

320

```scala

321

import scala.util.Using

322

323

def validateAndProcessFile(path: String): Either[String, ProcessedData] = {

324

Using(new FileInputStream(path)) { input =>

325

if (input.available() == 0) {

326

Left("File is empty")

327

} else {

328

val data = readData(input)

329

if (isValidData(data)) {

330

Right(processData(data))

331

} else {

332

Left("Invalid data format")

333

}

334

}

335

}.fold(

336

exception => Left(s"Error reading file: ${exception.getMessage}"),

337

identity

338

)

339

}

340

```

341

342

**Retry with Resources:**

343

344

```scala

345

import scala.util.Using

346

import scala.util.control.NonFatal

347

348

def withRetry[A](maxAttempts: Int)(operation: => A): Option[A] = {

349

(1 to maxAttempts).view.map { attempt =>

350

try {

351

Some(operation)

352

} catch {

353

case NonFatal(e) =>

354

if (attempt == maxAttempts) {

355

println(s"All $maxAttempts attempts failed")

356

None

357

} else {

358

println(s"Attempt $attempt failed, retrying...")

359

Thread.sleep(1000 * attempt) // Exponential backoff

360

None

361

}

362

}

363

}.find(_.isDefined).flatten

364

}

365

366

// Usage with resource management

367

withRetry(3) {

368

Using(connectToDatabase()) { conn =>

369

executeQuery(conn, "SELECT COUNT(*) FROM users")

370

}.get

371

}

372

```

373

374

### Advanced Patterns

375

376

**Resource Pooling:**

377

378

```scala

379

import scala.util.Using

380

import java.util.concurrent.BlockingQueue

381

382

class ResourcePool[R: Releasable](factory: () => R, maxSize: Int) {

383

private val pool: BlockingQueue[R] = new java.util.concurrent.LinkedBlockingQueue[R](maxSize)

384

385

def withResource[A](operation: R => A): A = {

386

val resource = Option(pool.poll()).getOrElse(factory())

387

try {

388

val result = operation(resource)

389

if (pool.remainingCapacity() > 0) {

390

pool.offer(resource)

391

} else {

392

implicitly[Releasable[R]].release(resource)

393

}

394

result

395

} catch {

396

case exception =>

397

implicitly[Releasable[R]].release(resource)

398

throw exception

399

}

400

}

401

}

402

403

// Usage

404

val dbPool = new ResourcePool(() => createDatabaseConnection(), 10)

405

406

dbPool.withResource { conn =>

407

executeQuery(conn, "SELECT * FROM products")

408

}

409

```

410

411

**Nested Resource Management:**

412

413

```scala

414

import scala.util.Using

415

416

def processNestedResources(): Unit = {

417

Using(openArchive("data.zip")) { archive =>

418

archive.entries.foreach { entry =>

419

Using(archive.getInputStream(entry)) { entryStream =>

420

Using(new BufferedReader(new InputStreamReader(entryStream))) { reader =>

421

processFileContent(entry.getName, reader)

422

}

423

}

424

}

425

}

426

}

427

```

428

429

The Using utility provides a robust foundation for resource management in Scala applications, ensuring that resources are properly cleaned up regardless of whether operations succeed or fail.