# `Skuld.Effects.Bracket`
[🔗](https://github.com/mccraigmccraig/skuld/blob/main/lib/skuld/effects/bracket.ex#L1)

Bracket effect for safe resource acquisition and cleanup.

Bracket ensures that resources are properly released even when errors occur.
This is essential for managing resources like file handles, database connections,
locks, and network connections.

## Overview

The `bracket/3` function takes three arguments:
- `acquire` - Computation that acquires a resource
- `release_fn` - Function `(resource -> computation)` that releases the resource
- `use_fn` - Function `(resource -> computation)` that uses the resource

The release function is guaranteed to run exactly once, whether the use
computation succeeds or throws an error.

## Example

    import Skuld.Syntax

    result <- Bracket.bracket(
      # Acquire resource
      comp do
        handle <- open_file("data.txt")
        handle
      end,
      # Release (always runs)
      fn handle ->
        comp do
          _ <- close_file(handle)
          :ok
        end
      end,
      # Use resource
      fn handle ->
        comp do
          content <- read_file(handle)
          process(content)
        end
      end
    )

## Error Handling

If the use computation throws an error, the release function runs before
the error is re-thrown:

    Bracket.bracket(
      acquire_connection(),
      fn conn -> release_connection(conn) end,
      fn conn ->
        comp do
          # If this throws, conn is still released
          result <- dangerous_operation(conn)
          result
        end
      end
    )

If the release function itself throws:
- If the use computation succeeded, the release error propagates
- If the use computation threw, the original error propagates (release error is suppressed)

## Nested Brackets

Brackets can be nested. Each bracket manages its own resource independently:

    Bracket.bracket(
      acquire_outer(),
      fn outer -> release_outer(outer) end,
      fn outer ->
        comp do
          inner_result <- Bracket.bracket(
            acquire_inner(),
            fn inner -> release_inner(inner) end,
            fn inner -> use_both(outer, inner) end
          )
          inner_result
        end
      end
    )

Resources are released in LIFO order (inner first, then outer).

## Convenience Functions

- `bracket/3` - Full bracket with acquire, release, and use
- `bracket_/2` - Simplified bracket when acquire returns the resource directly
- `finally/2` - Just ensure cleanup runs (no resource passing)

# `bracket`

```elixir
@spec bracket(
  Skuld.Comp.Types.computation(),
  (term() -&gt; Skuld.Comp.Types.computation()),
  (term() -&gt;
     Skuld.Comp.Types.computation())
) ::
  Skuld.Comp.Types.computation()
```

Acquire a resource, use it, and ensure it is released.

The release function is guaranteed to run exactly once, whether the use
computation succeeds or throws an error.

## Parameters

- `acquire` - Computation that acquires a resource
- `release_fn` - Function `(resource -> computation)` that releases the resource
- `use_fn` - Function `(resource -> computation)` that uses the resource

## Returns

A computation that returns the result of the use function.

## Example

    Bracket.bracket(
      comp do
        Logger.info("Acquiring lock")
        lock <- Lock.acquire(:my_lock)
        lock
      end,
      fn lock ->
        comp do
          Logger.info("Releasing lock")
          _ <- Lock.release(lock)
          :ok
        end
      end,
      fn lock ->
        comp do
          # Critical section - lock is held
          result <- do_work()
          result
        end
      end
    )

# `bracket_`

```elixir
@spec bracket_(term(), (term() -&gt; Skuld.Comp.Types.computation()), (term() -&gt;
                                                                Skuld.Comp.Types.computation())) ::
  Skuld.Comp.Types.computation()
```

Simplified bracket when acquire computation directly returns the resource.

This is a convenience wrapper when you don't need a separate acquire computation.

## Example

    # Instead of:
    Bracket.bracket(
      file_handle,
      fn h -> close(h) end,
      fn h -> read(h) end
    )

    # You can write:
    Bracket.bracket_(
      file_handle,
      fn h -> close(h) end,
      fn h -> read(h) end
    )

# `finally`

```elixir
@spec finally(Skuld.Comp.Types.computation(), Skuld.Comp.Types.computation()) ::
  Skuld.Comp.Types.computation()
```

Ensure cleanup runs after a computation, regardless of success or failure.

This is like `bracket` but without resource passing - just ensures the
cleanup computation runs.

## Example

    Bracket.finally(
      comp do
        _ <- Logger.info("Starting operation")
        result <- do_work()
        result
      end,
      comp do
        _ <- Logger.info("Operation complete")
        :ok
      end
    )

---

*Consult [api-reference.md](api-reference.md) for complete listing*
