Skip to content

Instantly share code, notes, and snippets.

@timjstewart
Created June 23, 2015 04:04
Show Gist options
  • Select an option

  • Save timjstewart/c733fd9d36d9781f258b to your computer and use it in GitHub Desktop.

Select an option

Save timjstewart/c733fd9d36d9781f258b to your computer and use it in GitHub Desktop.
A Java class similar to Haskell's Either.
package com.timjstewart;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.NoSuchElementException;
/**
* A value that can either be a value or a exception.
*
* @param <T> the type of the successful value.
*/
public abstract class Result<T> {
/**
* A function that is not defined for every element in its domain (e.g. a partial function).
* <p>
* <p>
* If the partial function is called with a value that is not in its domain, it throws an exception.
*
* @param <T> the type of the function's parameter.
* @param <U> the type of the function's return value
*/
@FunctionalInterface
public interface PartialFunction<T, U> {
U apply(T object) throws Exception;
}
/**
* Creates a successful {@code Result<T>}
*
* @param success the successful value
* @param <T> the successful value's type
* @return a successful {@code Result<T>} whose value is {@code value}.
*/
public static <T> Result<T> success(final T success) {
return new Success<>(success);
}
/**
* Creates a failed {@code Result<T>}
*
* @param failure the exception (an {@code Exception})
* @return a failed {@code Result<T>} whose value is an {@code Exception}.
*/
public static <T> Result<T> failure(final Exception failure) {
return new Failure<>(failure);
}
/**
* If this object is of type {@code Success<T>}, this method applies {@code func} to this object's
* value and returns the result wrapped in a {@code Result<T>}.
* <p>
* Otherwise, this method just returns this object so that the original exception is propagated through.
*
* @param func a partial function that can throw an Exception
* @param <U> the return type of {@code func}.
*
* @return a {@code Result} that wrap the result of calling func.
*/
public abstract <U> Result<U> map(PartialFunction<? super T, ? extends U> func);
public abstract <U> Result<U> flatMap(PartialFunction<? super T, Result<U>> func);
public static <U> Result<U> of(final Optional<U> optional) {
Objects.requireNonNull(optional);
if (optional.isPresent()) {
return success(optional.get());
} else {
return failure(new NoSuchElementException("No value present"));
}
}
/**
* Indicates whether or not this object represents a {@code Success}.
*
* @return true iff this object is a @{code Success}.
*/
public abstract boolean getValue();
/**
* Indicates whether or not this object represents a {@code Failure}.
*
* @return true iff this object is a @{code Failure}.
*/
public abstract boolean getException();
/**
* @return this object as a {@code Success<T>} iff if this object really is a {@code Success<T>}.
*
* @throws IllegalStateException if this object is not a {@code Success<T>}
*/
public abstract Success<T> asSuccess();
/**
* @return this object as a {@code Failure<T>} iff if this object really is a {@code Failure<T>}.
*
* @throws IllegalStateException if this object is not a {@code Failure<T>}
*/
public abstract Failure<T> asFailure();
public abstract Optional<T> asOptional();
public abstract T get() throws Exception;
public abstract void ifPresent(Consumer<? super T> consumer);
/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
*/
public abstract T orElse(T other);
final static class Success<T> extends Result<T> {
private final T value;
public Success(final T value) {
this.value = Objects.requireNonNull(value);
}
@Override final public <U> Result<U> map(final PartialFunction<? super T, ? extends U> func) {
try {
return success(func.apply(value));
} catch (Exception ex) {
return failure(ex);
}
}
@Override final public <U> Result<U> flatMap(final PartialFunction<? super T, Result<U>> func) {
try {
return func.apply(value);
} catch (Exception ex) {
return failure(ex);
}
}
@Override public final String toString() { return "Success [ value=" + value + "]"; }
@Override public final boolean getValue() { return true; }
@Override public final boolean getException() { return false; }
@Override public final Success<T> asSuccess() { return this; }
@Override public final Failure<T> asFailure() {
throw new IllegalStateException("not a Failure");
}
@Override public final T get() throws Exception { return value; }
@Override public final Optional<T> asOptional() { return Optional.of(value); }
@Override public final void ifPresent(Consumer<? super T> consumer) { consumer.accept(value); }
/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
*/
@Override public final T orElse(T other) {
return value;
}
}
final static class Failure<T> extends Result<T> {
private final Exception exception;
public Failure(Exception exception) {
this.exception = Objects.requireNonNull(exception);
}
@Override public final <U> Result<U> map(PartialFunction<? super T, ? extends U> func) {
return failure(exception);
}
@Override public final <U> Result<U> flatMap(PartialFunction<? super T, Result<U>> func) {
return failure(exception);
}
@Override public final String toString() { return "Failure [exception=" + exception + "]"; }
@Override public final boolean getValue() { return false; }
@Override public final boolean getException() { return true; }
@Override public final Success<T> asSuccess() {
throw new IllegalStateException("not a Success");
}
@Override public final Failure<T> asFailure() { return this; }
@Override public final T get() throws Exception { throw exception; }
@Override public final Optional<T> asOptional() { return Optional.empty(); }
@Override public final void ifPresent(Consumer<? super T> consumer) { }
/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
*/
@Override public final T orElse(T other) {
return other;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment