I also was fascinated by the presentation pointed out. I will add here some part of the code that I am using for handling checked exception (for these purposes I recommend taking a look at this presentation :
Exception Handling in Functional and Reactive Programming by Venkat Subramaniam )
First of all this is how I call it :
import static Monader.fnMaybeToMaybe;
import static Monader.fnOnValue;
import static Monader.fnValueToMaybe;
import java.util.List;
import org.junit.jupiter.api.Test;
public class RailTrackRunner {
@Test
public void goingThruSomeClassNames() {
List<String> classNames = List.of(
"lombok.Getter",
"java.util.concurrent.ConcurrentLinkedQueue",
"dummy.vrs.invalid",
"lombok.extern.slf4j.Slf4j");
System.out.println("\nV1");
classNames.stream()
.map(Maybe::ofValue)
.map(fnMaybeToMaybe(Class::forName)) // throws ClassNotFoundException
.map(fnOnValue(Class::getSimpleName))
.forEach(System.out::println);
System.out.println("\nV2");
classNames.stream()
.map(fnValueToMaybe(Class::forName)) // throws ClassNotFoundException
.map(fnOnValue(Class::getSimpleName))
.forEach(System.out::println);
}
}
and the necessary pieces are these:
@FunctionalInterface
public interface ExceptionThrower<T, R, EX extends Exception> {
R apply(T t) throws EX;
}
----- ----- ----- ----- ----- -----
import lombok.NonNull;
import lombok.Value;
@Value
public class Maybe<VAL, EXC extends Exception> {
public static <STVAL, STEXC extends Exception> Maybe<STVAL, STEXC> ofValue(@NonNull STVAL out) {
return new Maybe<STVAL, STEXC>(out, null);
}
public static <STVAL, STEXC extends Exception> Maybe<STVAL, STEXC> ofExc(@NonNull STEXC exc) {
return new Maybe<STVAL, STEXC>(null, exc);
}
private VAL value;
private EXC exc;
private Maybe(VAL value, EXC exc) {
this.value = value;
this.exc = exc;
if (!(hasValue() ^ hasException()))
throw new IllegalArgumentException("Invalid state for Maybe");
}
public boolean hasValue() {
return value != null;
}
public boolean hasException() {
return exc != null;
}
}
----- ----- ----- ----- ----- -----
import java.util.function.Function;
public class Monader<IN, OUT, EX extends Exception> {
public static <IN, OUT, EX extends Exception> Function<Maybe<IN, EX>, Maybe<OUT, EX>> fnOnValue(Function<IN, OUT> simpleValueFunction) {
return maybeIn -> {
if (maybeIn.hasValue()) {
OUT outValue = simpleValueFunction.apply(maybeIn.getValue());
return Maybe.ofValue(outValue);
} else // maybeIn.hasException()
return Maybe.ofExc(maybeIn.getExc());
};
}
public static <IN, OUT, EX extends Exception> Function<Maybe<IN, EX>, Maybe<OUT, EX>> fnMaybeToMaybe(ExceptionThrower<IN, OUT, EX> exceptionThrower) {
return maybeIn -> {
if (maybeIn.hasValue())
return executeThrower(exceptionThrower, maybeIn.getValue());
else // maybeIn.hasException()
return Maybe.ofExc(maybeIn.getExc());
};
}
public static <IN, OUT, EX extends Exception> Function<IN, Maybe<OUT, EX>> fnValueToMaybe(ExceptionThrower<IN, OUT, EX> exceptionThrower) {
return in -> executeThrower(exceptionThrower, in);
}
private static <IN, OUT, EX extends Exception> Maybe<OUT, EX> executeThrower(ExceptionThrower<IN, OUT, EX> exceptionThrower, IN in) {
try {
OUT out = exceptionThrower.apply(in);
return Maybe.ofValue(out);
} catch (RuntimeException rexc) {
throw rexc;
} catch (Exception exc) {
try {
@SuppressWarnings("unchecked")
EX castedEsc = (EX) exc;
return Maybe.ofExc(castedEsc);
} catch (ClassCastException cce) {
throw new RuntimeException(exc);
}
}
}
}