# Result

> Result is used to represent the outcome of an operation explicitly instead of returning `null` or branching through scattered success and failure checks.

`Result` stores either a success value or a failure reason in one predictable type. `ResultAsync` provides the same API for `CompletableFuture`-based operations.

This removes the need to pass raw values, `null`, and separate error flags through multiple layers of code. It also makes success, failure, recovery, and transformation logic easier to follow.

{% hint style="info" %}
Result is mainly used for service operations, validation, lookups, and asynchronous workflows where success and failure should stay explicit and chainable.
{% endhint %}

***

### Result creation - `Results`

`Results` provides factory methods for synchronous and asynchronous result creation.

Use `Results.ofSuccess` and `Results.ofFailure` when the final state is already known. Use `Results.sync` and `Results.async` when the result should be decided by predicates.

#### Direct creation - Example

```java
enum UserFailure implements ResultReason.Failure {
  USER_NOT_FOUND,
  INVALID_NAME
}

Result<String> success = Results.ofSuccess("ranked");
Result<String> failure = Results.ofFailure(UserFailure.USER_NOT_FOUND);
```

`Results.ofSuccess` creates a successful `Result` with the provided value. `Results.ofFailure` creates a failed `Result` with `null` as the value and the provided failure reason.

For asynchronous workflows, `Results.ofSuccessAsync` wraps a `CompletableFuture<T>` as a successful `ResultAsync`. `Results.ofFailureAsync` creates an already failed `ResultAsync`.

```java
CompletableFuture<String> futureName = CompletableFuture.completedFuture("ranked");

ResultAsync<String> asyncSuccess = Results.ofSuccessAsync(futureName);
ResultAsync<String> asyncFailure = Results.ofFailureAsync(UserFailure.USER_NOT_FOUND);
```

***

### Success and failure handling - `success` and `failure`

`success` runs a consumer only when the result is successful. `failure` runs a consumer only when the result is failed.

```java
Results.ofSuccess("ranked")
    .success(name -> IO.println("Loaded user: " + name))
    .failure(reason -> IO.println("Failed: " + reason));
```

When this chain runs, only the `success` consumer is executed. The `failure` consumer is skipped.

For a failed result, the opposite happens:

```java
Results.<String>ofFailure(UserFailure.USER_NOT_FOUND)
    .success(name -> IO.println("Loaded user: " + name))
    .failure(reason -> IO.println("Failed: " + reason));
```

When this chain runs, only the `failure` consumer is executed. The `success` consumer is skipped.

#### Matching a specific failure reason - Example

`failure(reason, consumer)` runs only when the failure matches the provided reason.

```java
Results.<String>ofFailure(UserFailure.USER_NOT_FOUND)
    .failure(UserFailure.USER_NOT_FOUND, reason -> {
        IO.println("User does not exist");
    });
```

This is useful when different failure reasons need different handling without adding manual `if` checks around the result.

***

### Value transformation - `mapSuccess` and `mapFailure`

`mapSuccess` transforms the success value and keeps the failure state unchanged. `mapFailure` recovers from a failure by transforming the failure reason into a success value.

```java
Result<String> normalized = Results.ofSuccess("ranked")
    .mapSuccess(String::toUpperCase);
```

If the original result is successful, the new `Result` contains the transformed value. If the original result is failed, the same failure is preserved.

#### Recovering from failure - Example

```java
Result<String> recovered = Results.<String>ofFailure(UserFailure.USER_NOT_FOUND)
    .mapFailure(reason -> "guest");
```

When `mapFailure` is used on a failed result, the returned result becomes successful with the produced value. When it is used on a successful result, the original success is kept.

***

### Predicate-based result building - `sync` and `async`

`Results.sync` and `Results.async` create a builder that decides success or failure using predicates.

`successIf` marks the value as successful when any success predicate matches. `failIf` marks the value as failed when a failure predicate matches and provides the failure reason.

#### Synchronous validation - Example

```java
Result<String> result = Results.sync("ranked")
    .successIf(name -> name != null && !name.isBlank())
    .failIf(String::isBlank, UserFailure.INVALID_NAME)
    .build();
```

The builder checks `successIf` first. If any success predicate matches, the result is successful immediately.

If no success predicate matches, the builder checks `failIf` predicates in order and uses the first matching failure reason. If nothing matches, the result is returned as success with the original value.

#### Asynchronous validation - Example

```java
CompletableFuture<String> futureName = CompletableFuture.completedFuture("ranked");

ResultAsync<String> result = Results.async(futureName)
    .successIf(name -> name != null && !name.isBlank())
    .failIf(value -> value == null, UserFailure.USER_NOT_FOUND)
    .build();
```

When the future completes, the builder evaluates the predicates and creates the final `ResultAsync`.

***

### Asynchronous chaining - `ResultAsync`

`ResultAsync` provides the same `success`, `failure`, `mapSuccess`, and `mapFailure` operations as `Result`, but applies them after the wrapped `CompletableFuture` completes.

```java
Results.ofSuccessAsync(CompletableFuture.completedFuture("ranked"))
    .mapSuccess(String::toUpperCase)
    .success(name -> IO.println("Loaded user: " + name))
    .failure(reason -> IO.println("Failed: " + reason));
```

Each operation returns a new `ResultAsync`. This allows the full success and failure chain to stay explicit while the value is still asynchronous.

#### Chaining a new async operation - `flatMapSuccess`

`flatMapSuccess` starts a new asynchronous operation only when the current result is successful.

```java
Results.ofSuccessAsync(CompletableFuture.completedFuture("ranked"))
    .flatMapSuccess(name -> CompletableFuture.completedFuture(name.length()))
    .success(length -> IO.println("Name length: " + length));
```

If the current result is failed, the failure is preserved and the new async operation is skipped.

#### Async recovery - `flatMapFailure`

`flatMapFailure` starts a new asynchronous recovery operation only when the current result is failed.

```java
Results.<String>ofFailureAsync(UserFailure.USER_NOT_FOUND)
    .flatMapFailure(reason -> CompletableFuture.completedFuture("guest"))
    .success(name -> IO.println("Loaded user: " + name));
```

If the current result is successful, the success value is preserved and the recovery operation is skipped.

***

### State access

`Result.getValue` returns the contained value. `Result.getReason` returns the current reason. `Result.isSuccess` and `Result.isFailure` expose the current state directly.

```java
Result<String> result = Results.ofSuccess("ranked");

result.getValue();
result.getReason();
result.isSuccess();
result.isFailure();
```

For asynchronous results, `ResultAsync.getValue` exposes the success value as a `CompletableFuture`. `ResultAsync.isSuccess` and `ResultAsync.isFailure` return `CompletableFuture<Boolean>`.

```java
ResultAsync<String> result = Results.ofSuccessAsync(CompletableFuture.completedFuture("ranked"));

result.getValue();
result.isSuccess();
result.isFailure();
```

***

### Execution model

{% hint style="info" %}
`Result` runs synchronously on the caller's thread. `ResultAsync` does not create its own threads. It applies operations when the wrapped `CompletableFuture` completes.
{% endhint %}

`Result` is immediate and in-memory. Each chained call returns the same result instance or a new transformed result, depending on the operation.

`ResultAsync` keeps the same outcome model, but delays execution until the underlying future resolves. This keeps asynchronous control flow explicit without switching to manual `CompletableFuture` branching in every call site.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ranked.wtf/core-concepts/result.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
