Skip to content

Instantly share code, notes, and snippets.

@gszeliga
Last active October 26, 2015 23:34
Show Gist options
  • Save gszeliga/e81341dc165a2b3f1fa1 to your computer and use it in GitHub Desktop.
Save gszeliga/e81341dc165a2b3f1fa1 to your computer and use it in GitHub Desktop.
import Either;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
public class Outcome<T> {
private Either<List<Failure>, Optional<T>> either;
public static <T> Outcome<T> maybe(Optional<T> maybeSuccess) {
Outcome<T> result = new Outcome<>();
result.either = Either.right(maybeSuccess);
return result;
}
public static <T> Outcome<List<T>> sequence(List<Outcome<T>> outcomes){
final Outcome<List<T>> unit = Outcome.maybe(new ArrayList<>(outcomes.size()));
return outcomes
.stream()
.reduce(unit,
//Extract T instance and add it to unit, otherwise combine errors
(o1, o2) ->
o1.flatMapR(
l -> o2.mapR(ec -> {
l.add(ec);
return l;
})
).dependingOn(o2),
//Merge all results into one
(ol1, ol2) ->
ol1.flatMapR(l1 -> ol2.mapR(l2 -> {
final List<T> ll = new ArrayList<>(l1.size() + l2.size());
ll.addAll(l1);
ll.addAll(l2);
return ll;
})));
}
public static <T> Outcome<T> maybe(T maybeSuccess) {
return maybe(Optional.ofNullable(maybeSuccess));
}
public static <T> Outcome<T> emptyMaybe() {
return maybe(Optional.<T>empty());
}
public static <T> Outcome<T> failure(final List<Failure> failures) {
Outcome<T> result = new Outcome<>();
result.either = Either.left(failures);
return result;
}
public static <T> Outcome<T> failure(final Failure failure) {
Outcome<T> result = new Outcome<>();
result.either = Either.left(Arrays.asList(failure));
return result;
}
public boolean isMaybe() {
return either.isRight();
}
public boolean isMaybePresent() {
if (either.isRight()) {
return either.getRight().isPresent();
} else {
return false;
}
}
public boolean isFailure() {
return either.isLeft();
}
public T maybe() {
if (either.isRight() && either.getRight().isPresent()) {
return either.getRight().get();
} else {
throw new NoSuchElementException("No value present");
}
}
public List<Failure> failures() {
if (either.isLeft()) {
return either.getLeft();
} else {
throw new NoSuchElementException("No value present");
}
}
public final <TT> Outcome<TT> mapR(Function<T, TT> transform) {
if (isMaybe()) {
return maybe(this.either.getRight().map(transform));
} else {
return (Outcome<TT>) this;
}
}
public final <TT> Outcome<TT> mapL(Function<List<Failure>, List<Failure>> add) {
if (isFailure()) {
return (Outcome<TT>) failure(add.apply(failures()));
} else {
return (Outcome<TT>) this;
}
}
public final <TT> Outcome<TT> flatMapR(Function<T, Outcome<TT>> function) {
if (isMaybe() && this.either.getRight().isPresent()) {
return function.apply(this.either.getRight().get());
} else {
return (Outcome<TT>) this;
}
}
public final <TT> Outcome<TT> inferred()
{
return (Outcome<TT>)this;
}
public final <U> Outcome<T> dependingOn(Outcome<U> that) {
if (this.isFailure() || that.isFailure()) {
if(this.isFailure() && that.isFailure()){
List<Failure> failures = new ArrayList<>(that.failures());
//Add only those failures that doesn't exist (avoid repeated values)
failures()
.stream()
.filter(f -> !failures.contains(f))
.forEach(failures::add);
return failure(failures);
}
else if(that.isFailure())
{
return (Outcome<T>)that;
}
else
{
return this;
}
}
else {
return this;
}
}
public final <TT> Outcome<TT> fold(Function<List<Failure>, TT> failure, Function<T, TT> success) {
if (isMaybe()) {
return maybe(this.either.getRight().map(success));
} else {
return maybe(Optional.ofNullable(failure.apply(this.either.getLeft())));
}
}
public final T orElseGet(Supplier<T> otherValue) {
if (isMaybe() && either.getRight().isPresent()) {
return either.getRight().get();
} else {
return otherValue.get();
}
}
public final <TT> Outcome<TT> orIfEmpty(Supplier<Failure> failure) {
if (isMaybePresent()) {
return (Outcome<TT>)this;
} else {
final ArrayList<Failure> newfailures;
if(isFailure()) {
newfailures = new ArrayList<>(failures());
newfailures.add(failure.get());
}
else
{
newfailures = new ArrayList<>(1);
newfailures.add(failure.get());
}
return failure(newfailures);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment