or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

concurrency.mdcore-io.mdindex.mdresources.mdruntime.mdstd.mdtime.md
tile.json

resources.mddocs/

Resource Management

Safe resource acquisition, usage, and cleanup with the Resource data type and bracket patterns. Resources ensure that cleanup actions are always executed, even in the face of exceptions or cancellation.

Capabilities

Resource Construction

Create resources from acquisition and release operations.

/**
 * Create resource from acquisition and release operations
 * @param acquire - IO to acquire the resource
 * @param release - Function to release the resource
 * @returns Resource[IO, A] managing the lifecycle
 */
def Resource.make[A](acquire: IO[A])(release: A => IO[Unit]): Resource[IO, A]

/**
 * Create resource from acquisition and release with outcome information
 * @param acquire - IO to acquire the resource
 * @param release - Function to release based on outcome
 * @returns Resource[IO, A] with outcome-aware cleanup
 */
def Resource.makeCase[A](acquire: IO[A])(
  release: (A, Outcome[IO, Throwable, ?]) => IO[Unit]
): Resource[IO, A]

/**
 * Create resource from acquisition and release with full outcome control
 * @param acquire - IO to acquire the resource  
 * @param release - Function with full outcome and cleanup control
 * @returns Resource[IO, A] with maximum control over cleanup
 */
def Resource.makeFull[A](acquire: Poll[IO] => IO[A])(
  release: (A, Outcome[IO, Throwable, ?]) => IO[Unit]
): Resource[IO, A]

/**
 * Lift pure value into Resource
 * @param a - Pure value
 * @returns Resource[IO, A] containing the value
 */
def Resource.pure[A](a: A): Resource[IO, A]

/**
 * Lift IO into Resource (no cleanup needed)
 * @param fa - IO to lift
 * @returns Resource[IO, A] wrapping the IO
 */
def Resource.eval[A](fa: IO[A]): Resource[IO, A]

/**
 * Create finalizer-only resource (cleanup without acquisition)
 * @param finalizer - Cleanup IO to run
 * @returns Resource[IO, Unit] that runs cleanup
 */
def Resource.onFinalize(finalizer: IO[Unit]): Resource[IO, Unit]

Resource Usage

Primary operations for using resources safely.

/**
 * Use resource safely with automatic cleanup
 * @param f - Function that uses the resource
 * @returns IO[B] with guaranteed resource cleanup
 */
def use[B](f: A => IO[B]): IO[B]

/**
 * Allocate resource, returning value and cleanup action
 * @returns IO[(A, IO[Unit])] with resource and manual cleanup
 */
def allocated: IO[(A, IO[Unit])]

/**
 * Use resource to compute another resource
 * @param f - Function from resource to next resource computation
 * @returns Resource[IO, B] with both resources managed
 */
def flatMap[B](f: A => Resource[IO, B]): Resource[IO, B]

/**
 * Transform resource value
 * @param f - Transformation function
 * @returns Resource[IO, B] with transformed value
 */
def map[B](f: A => B): Resource[IO, B]

/**
 * Apply effectful transformation to resource
 * @param f - Effectful transformation
 * @returns Resource[IO, B] with effect applied during acquisition
 */
def evalMap[B](f: A => IO[B]): Resource[IO, B]

/**
 * Apply effectful operation without changing resource value
 * @param f - Side-effecting operation
 * @returns Resource[IO, A] with side effect applied
 */
def evalTap[B](f: A => IO[B]): Resource[IO, A]

Resource Composition

Combine and transform resources.

/**
 * Combine two resources in parallel
 * @param other - Other resource to combine with
 * @returns Resource[IO, (A, B)] with both resources
 */
def both[B](other: Resource[IO, B]): Resource[IO, (A, B)]

/**
 * Race two resources, using the winner
 * @param other - Resource to race against
 * @returns Resource[IO, Either[A, B]] with winning resource
 */
def race[B](other: Resource[IO, B]): Resource[IO, Either[A, B]]

/**
 * Use first resource around second resource  
 * @param other - Resource to surround
 * @returns Resource[IO, B] with first resource's lifecycle wrapping second
 */
def surround[B](other: Resource[IO, B]): Resource[IO, B]

/**
 * Add finalizer to resource cleanup
 * @param finalizer - Additional cleanup IO
 * @returns Resource[IO, A] with additional cleanup
 */
def onFinalize(finalizer: IO[Unit]): Resource[IO, A]

/**
 * Add finalizer with outcome information
 * @param finalizer - Cleanup function receiving outcome
 * @returns Resource[IO, A] with outcome-aware cleanup
 */
def guaranteeCase(finalizer: Outcome[IO, Throwable, ?] => IO[Unit]): Resource[IO, A]

Error Handling

Resource-specific error handling preserving cleanup guarantees.

/**
 * Handle errors in resource operations
 * @returns Resource[IO, Either[Throwable, A]] with errors as values
 */
def attempt: Resource[IO, Either[Throwable, A]]

/**
 * Handle errors with recovery resource
 * @param f - Function from error to recovery resource
 * @returns Resource[IO, AA] that recovers from errors
 */
def handleErrorWith[AA >: A](f: Throwable => Resource[IO, AA]): Resource[IO, AA]

/**
 * Transform both success and error cases
 * @param recover - Function to handle errors
 * @param map - Function to transform success
 * @returns Resource[IO, B] with both cases handled
 */
def redeem[B](recover: Throwable => B, map: A => B): Resource[IO, B]

/**
 * Transform both cases with resource operations
 * @param recover - Function from error to recovery resource
 * @param bind - Function from success to next resource
 * @returns Resource[IO, B] with both cases handled
 */
def redeemWith[B](
  recover: Throwable => Resource[IO, B], 
  bind: A => Resource[IO, B]
): Resource[IO, B]

Timeout and Cancellation

Apply timeouts and cancellation to resource operations.

/**
 * Apply timeout to resource operations
 * @param duration - Maximum time allowed
 * @returns Resource[IO, A] that times out after duration
 */
def timeout(duration: FiniteDuration): Resource[IO, A]

/**
 * Apply timeout with fallback
 * @param duration - Maximum time allowed
 * @param fallback - Resource to use on timeout  
 * @returns Resource[IO, AA] with timeout fallback
 */
def timeoutTo[AA >: A](
  duration: FiniteDuration, 
  fallback: Resource[IO, AA]
): Resource[IO, AA]

Bracket Operations

Lower-level bracket operations for resource patterns.

/**
 * Bracket pattern - acquire, use, release
 * @param acquire - IO to acquire resource
 * @param use - Function to use resource
 * @param release - Function to release resource
 * @returns IO[B] with guaranteed cleanup
 */
def IO.bracket[A, B](acquire: IO[A])(use: A => IO[B])(release: A => IO[Unit]): IO[B]

/**
 * Bracket with outcome-aware release
 * @param acquire - IO to acquire resource
 * @param use - Function to use resource
 * @param release - Release function receiving outcome
 * @returns IO[B] with outcome-aware cleanup
 */
def IO.bracketCase[A, B](acquire: IO[A])(use: A => IO[B])(
  release: (A, Outcome[IO, Throwable, B]) => IO[Unit]
): IO[B]

/**
 * Full bracket with polling support in acquisition
 * @param acquire - Acquisition with cancellation polling
 * @param use - Function to use resource
 * @param release - Release function with outcome
 * @returns IO[B] with full control over resource lifecycle
 */
def IO.bracketFull[A, B](acquire: Poll[IO] => IO[A])(use: A => IO[B])(
  release: (A, Outcome[IO, Throwable, B]) => IO[Unit]
): IO[B]

Usage Examples:

import cats.effect._
import java.io._
import scala.concurrent.duration._

// Basic file resource
val fileResource = Resource.make(
  IO.delay(new FileInputStream("data.txt"))
)(fis => IO.delay(fis.close()))

val readFile = fileResource.use { fis =>
  IO.delay {
    val buffer = new Array[Byte](1024)
    val bytesRead = fis.read(buffer)
    new String(buffer, 0, bytesRead)
  }
}

// Multiple resources with automatic composition
val multipleResources = for {
  input  <- Resource.make(IO.delay(new FileInputStream("input.txt")))(
              fis => IO.delay(fis.close()))
  output <- Resource.make(IO.delay(new FileOutputStream("output.txt")))(
              fos => IO.delay(fos.close()))
} yield (input, output)

val copyFile = multipleResources.use { case (input, output) =>
  IO.delay {
    val buffer = new Array[Byte](1024)
    var bytesRead = input.read(buffer)
    while (bytesRead != -1) {
      output.write(buffer, 0, bytesRead)
      bytesRead = input.read(buffer)
    }
  }
}

// Resource with error handling
val safeFileResource = Resource.make(
  IO.delay(new FileInputStream("might-not-exist.txt"))
)(fis => IO.delay(fis.close()))
  .handleErrorWith(_ => 
    Resource.eval(IO.println("File not found, using default")).as(null)
  )

// Connection pool example
case class Connection(id: Int) {
  def query(sql: String): IO[String] = IO.pure(s"Result for: $sql")
  def close(): IO[Unit] = IO.println(s"Closing connection $id")
}

val connectionResource = Resource.make(
  IO.delay(Connection(scala.util.Random.nextInt(1000)))
)(conn => conn.close())

val queryWithConnection = connectionResource.use { conn =>
  for {
    result1 <- conn.query("SELECT * FROM users")
    result2 <- conn.query("SELECT * FROM orders") 
    _       <- IO.println(s"Results: $result1, $result2")
  } yield (result1, result2)
}

// Resource with timeout
val timedResource = connectionResource
  .timeout(5.seconds)
  .use(conn => conn.query("SLOW QUERY"))