Skip to content

Instantly share code, notes, and snippets.

@mnicky
Created January 26, 2019 23:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mnicky/b443a3ff792b1a0524195d72f2c036ee to your computer and use it in GitHub Desktop.
Save mnicky/b443a3ff792b1a0524195d72f2c036ee to your computer and use it in GitHub Desktop.
Result monad for Java (WIP)
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
public class Result<SUCC, FAIL> {
private final SUCC success;
private final FAIL failure;
//TODO: test whether failureSuppliesr shouldn't rather be functions of all arguments
/* === static factories === */
public static <SUCC, FAIL> Result<SUCC, FAIL> success(SUCC success) {
return new Result<>(success, null);
}
public static <SUCC, FAIL> Result<SUCC, FAIL> failure(FAIL failure) {
return new Result<>(null, failure);
}
public static <SUCC, FAIL> Result<SUCC, FAIL> failure(Supplier<? extends FAIL> failure) {
return new Result<>(null, failure.get());
}
public static <SUCC, FAIL> Result<SUCC, FAIL> ofNullable(SUCC nullable, FAIL failure) {
return nullable == null ? failure(failure) : success(nullable);
}
public static <SUCC, FAIL> Result<SUCC, FAIL> ofNullable(SUCC nullable, Supplier<? extends FAIL> failureSupplier) {
return nullable == null ? failure(failureSupplier.get()) : success(nullable);
}
//TODO: is it correct to return exact FAIL type?s
public static <SUCC> Result<SUCC, NullPointerException> ofNullable(SUCC nullable, String failureMessage) {
return nullable == null ? failure(new NullPointerException(failureMessage)) : success(nullable);
}
/* === getters === */
public SUCC getSuccess() {
if (succeeded()) return success;
else throw new NoSuchElementException("Cannot get success of a failed result");
}
public FAIL getFailure() {
if (failed()) return failure;
else throw new NoSuchElementException("Cannot get failure of a succeeded result");
}
/* === methods === */
public boolean succeeded() {
return success != null;
}
public boolean failed() {
return failure != null;
}
public void ifSucceeded(Consumer<? super SUCC> consumer) {
if (succeeded()) consumer.accept(getSuccess());
}
public void ifFailed(Consumer<? super FAIL> consumer) {
if (failed()) consumer.accept(getFailure());
}
//TODO: This method doesn't allow to change FAIL type. Is this correct?
public Result<SUCC, FAIL> filter(Predicate<? super SUCC> predicate, FAIL failure) {
if (succeeded()) return predicate.test(getSuccess()) ? this : failure(failure);
else return failure(getFailure());
}
//TODO: This method doesn't allow to change FAIL type. Is this correct?
public Result<SUCC, FAIL> filter(Predicate<? super SUCC> predicate, Supplier<? extends FAIL> failureSupplier) {
if (succeeded()) return predicate.test(getSuccess()) ? this :failure(failureSupplier.get());
else return failure(getFailure());
}
//TODO: this method reuses failure from the previous call - makes it sense?
public <SUCC2> Result<SUCC2, FAIL> and(Function<? super SUCC, Result<SUCC2, FAIL>> mapper) {
if (succeeded()) return mapper.apply(getSuccess());
else return failure(getFailure());
}
//TODO: this method reuses failure from the previous call - makes it sense?
public <SUCC2> Result<SUCC2, FAIL> andResultOf(Function<? super SUCC, ? extends SUCC2> mapper) {
if (succeeded()) return success(mapper.apply(getSuccess()));
else return failure(getFailure());
}
//TODO: this method reuses failure from the previous call - makes it sense?
public <SUCC2> Result<SUCC2, FAIL> andResultOfNullable(Function<? super SUCC, ? extends SUCC2> mapper) {
if (succeeded()) return ofNullable(mapper.apply(getSuccess()), getFailure());
else return failure(getFailure());
}
//TODO: this method reuses failure from the previous call - makes it sense?
public <SUCC2> Result<SUCC2, FAIL> andResultOfNullable(Function<? super SUCC, ? extends SUCC2> mapper,
Supplier<? extends FAIL> failureSupplier)
{
if (succeeded()) return ofNullable(mapper.apply(getSuccess()), failureSupplier.get());
else return failure(getFailure());
}
//TODO: this method may change the type of FAIL. Is this correct?
public <SUCC2> Result<SUCC2, FAIL> andResultOfNullable(Function<? super SUCC, ? extends SUCC2> mapper,
String failureMessage)
{
if (succeeded()) return ofNullable(mapper.apply(getSuccess()), failureMessage);
else return failure(getFailure());
}
public <FAIL2> Result<SUCC, FAIL2> mapFailure(Function<? super FAIL, ? extends FAIL2> failureMapper) {
if (succeeded()) return success(getSuccess());
else return failure(failureMapper.apply(getFailure()));
}
public <SUCC2, FAIL2> Result<SUCC2, FAIL2> mapBoth(Function<? super SUCC, ? extends SUCC2> successMapper,
Function<? super FAIL, ? extends FAIL2> failureMapper)
{
if (succeeded()) return success(successMapper.apply(getSuccess()));
else return failure(failureMapper.apply(getFailure()));
}
public Result<SUCC, FAIL> or(SUCC other) {
if (succeeded()) return this;
else return Result.success(other);
}
public Result<SUCC, FAIL> or(Supplier<? extends SUCC> other) {
if (succeeded()) return this;
else return Result.success(other.get());
}
public <E extends Throwable> Result<SUCC, FAIL> orThrow(Supplier<? extends E> exceptionSupplier) throws E {
if (succeeded()) return this;
else throw exceptionSupplier.get();
}
public Result<SUCC, FAIL> orThrow(String exceptionMessage) throws RuntimeException {
if (succeeded()) return this;
else throw new RuntimeException(exceptionMessage);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment