Skip to content

Instantly share code, notes, and snippets.

@stefanofago73
Last active April 9, 2025 13:40
Show Gist options
  • Select an option

  • Save stefanofago73/d7e6ba75ba1d2fb0a10d64bbf9a05e58 to your computer and use it in GitHub Desktop.

Select an option

Save stefanofago73/d7e6ba75ba1d2fb0a10d64bbf9a05e58 to your computer and use it in GitHub Desktop.
A simplistic implementation of the Either functional abstraction but using RuntimeException
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
//
// The idea is that the RuntimeException is the Left barnch but implicit!
//
// Exceptions can be Generic, so the "simulation" is partial...
// A better implementation can be using a "type" expressed by
// a Generic Interface that needs to be "instatiated" in the implements moment:
// For Example: EitherRuntimeException implements Either<String>
//
class Either extends RuntimeException {
private final Object value;
private boolean isLeft;
// Constructor for Left (error)
public Either(String msg) {
super(msg);
this.value = this;
this.isLeft = true;
}
//
// Constructor for Right (normal)
// It's a Generic Constructor to force a type at least in the construction point
//
public <T> Either(T value) {
super(null, null, false, false); // force the exception to be only throwable but less effective "as exception"
this.value = value;
}
// Static factory for Left
public static Either left(String msg) {
return new Either(msg);
}
// Static factory for Right
public static <R> Either right(R value) {
return new <R> Either(value);
}
public boolean isLeft() {
return isLeft;
}
public boolean isRight() {
return !isLeft;
}
public RuntimeException getLeft() {
if (!isLeft) throw new IllegalStateException("Not a Left value");
return this;
}
public <R> R getRight() {
if (isLeft) throw new IllegalStateException("Not a Right value");
@SuppressWarnings("unchecked")
R result = (R) value;
return result;
}
public <T,R> Either map(Function<R, T> mapper) {
if (isRight()) {
try {
return new Either(mapper.apply((R)this.value));
} catch (Exception e) {
return new Either(e.getMessage());
}
} else {
return this;
}
}
public <R> Either flatMap(Function<R, Either> mapper) {
if (isRight()) {
try {
return mapper.apply((R)this.value);
} catch (Exception e) {
return new Either(e.getMessage());
}
} else {
return this;
}
}
public <R> R orElse(R defaultValue) {
return isRight() ? getRight() : defaultValue;
}
// orElseGet: Supplier for fallback
public <R> R orElseGet(Supplier<R> supplier) {
return isRight() ? getRight() : supplier.get();
}
// orElseThrow: propagate left as RuntimeException
public <R> R orElseThrow() {
if (isRight()) return getRight();
throw this;
}
@Override
public String toString() {
return isLeft ? "Left(" + value + ")" : "Right(" + value + ")";
}
}
public class Demo {
public static void main(String args[]) {
Either a = Either.left("Invalid input");
Either b = Either.right(10);
Either result = b
.map(x -> (int)x + 5)
.map(x -> "Result: " + x);
System.out.println(result); // Right(Result: 15)
Either finalResult = b.flatMap(x -> {
if ((int)x > 5) return Either.right("Big enough");
else return Either.left("Too small");
});
System.out.println(finalResult); // Right(Big enough)
// Error propagation
try {
a.orElseThrow();
} catch (Exception e) {
System.out.println("Caught: " + e.getMessage()); // Caught: Invalid input
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment