# oi

`software.visionary:oi:2026.10.13` · https://codeberg.org/javanese/oi

Oi: checked IO a stream can carry, failed unchecked

<code>IoFunction</code> and <code>IoConsumer</code> let a stream map through and end in IO: each is a <code>Function</code> or <code>Consumer</code> whose work throws <code>IOException</code>, rethrown as <code>UncheckedIOException</code>.

## Rationale

**Why.** A stream's <code>Function</code> and <code>Consumer</code> cannot throw <code>IOException</code>, yet reading or writing a file is often the step a stream needs.

## Key Concepts

### Checked IO in a chain

**Role.** A lambda cast to <code>IoFunction&lt;T, R&gt;</code> or <code>IoConsumer&lt;T&gt;</code> implements <code>io</code>, which may throw <code>IOException</code>, and is the <code>Function</code> or <code>Consumer</code> a stream's <code>map</code> or <code>forEach</code> takes.

**Pattern.** Template Method

## How It Works

**Guarantees.** An <code>IOException</code> thrown by <code>io</code> leaves <code>apply</code> or <code>accept</code> as an <code>UncheckedIOException</code> whose cause is that <code>IOException</code>. No method takes null and <code>IoFunction</code> never returns it, declared with contrax's <code>@PreCondition(IsNotNull.class)</code> and <code>@PostCondition(IsNotNull.class)</code>.

## Lane

**Purpose.** Mapping through and ending a stream in IO, where any IO failure stops the stream.

**Alternative.** A loop with its own <code>try</code>/<code>catch</code>, when one element's IO failure is recovered from and the rest go on.

## Usage

Without oi: a try/catch in every lambda

```java
places.stream()
        .map(place -> {
            try {
                return place.toRealPath();
            } catch (final IOException failed) {
                throw new UncheckedIOException(failed);
            }
        })
        .forEach(place -> {
            try {
                Files.delete(place);
            } catch (final IOException failed) {
                throw new UncheckedIOException(failed);
            }
        });
```

With oi: the lambda is cast, and throws

```java
places.stream()
        .map((IoFunction<Path, Path>) Path::toRealPath)
        .forEach((IoConsumer<Path>) Files::delete);
```

## Dependencies

- `software.visionary:junit-closure:2026.09.17` at Testing

