Last active
April 9, 2025 13:40
-
-
Save stefanofago73/d7e6ba75ba1d2fb0a10d64bbf9a05e58 to your computer and use it in GitHub Desktop.
A simplistic implementation of the Either functional abstraction but using RuntimeException
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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