En lenguajes funcionales (y en C++23 con std::expected), existe un tipo que representa éxito o error sin excepciones.
En Java podemos implementarlo fácilmente y hacerlo monádico.
Result<T, E> encapsula dos posibles estados:
- Ok(T) → el cálculo fue exitoso
- Err(E) → ocurrió un error
Así evitamos lanzar excepciones, y podemos encadenar operaciones de manera declarativa.
public sealed interface Result<T, E> permits Ok, Err {
boolean isOk();
boolean isErr();
<U> Result<U, E> map(Function<? super T, ? extends U> f);
<U> Result<U, E> flatMap(Function<? super T, Result<U, E>> f);
T orElse(T fallback);
}
public record Ok<T, E>(T value) implements Result<T, E> {
public boolean isOk() { return true; }
public boolean isErr() { return false; }
public <U> Result<U, E> map(Function<? super T, ? extends U> f) {
return new Ok<>(f.apply(value));
}
public <U> Result<U, E> flatMap(Function<? super T, Result<U, E>> f) {
return f.apply(value);
}
public T orElse(T fallback) { return value; }
}
public record Err<T, E>(E error) implements Result<T, E> {
public boolean isOk() { return false; }
public boolean isErr() { return true; }
public <U> Result<U, E> map(Function<? super T, ? extends U> f) {
return new Err<>(error);
}
public <U> Result<U, E> flatMap(Function<? super T, Result<U, E>> f) {
return new Err<>(error);
}
public T orElse(T fallback) { return fallback; }
}
Podemos usarlo, de esta manera:
Result<Integer, String> parseInt(String s) {
try {
return new Ok<>(Integer.parseInt(s));
} catch (NumberFormatException e) {
return new Err<>("Not a number");
}
}
Result<Integer, String> divideByTwo(int n) {
return (n % 2 == 0)
? new Ok<>(n / 2)
: new Err<>("Odd number");
}
var result = parseInt("42")
.flatMap(this::divideByTwo)
.map(x -> x * 3)
.orElse(0);
System.out.println(result); // 63
Si en cualquier paso se produce un error (Err), las transformaciones se detienen automáticamente.
No hay try/catch, ni comprobaciones manuales de estado.
Ventajas del enfoque monádico
- Evita excepciones y null.
- Encadena operaciones de manera natural.
- Explicita el flujo de éxito/error.
- Es fácilmente testeable y composable.
En Java no hace falta un lenguaje funcional completo para pensar funcionalmente.
Con un poco de sintaxis moderna (record, sealed interface, lambdas) podés crear tus propios tipos monádicos.














.jpeg)


.png)
.png)
.png)




