All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cyclops.control.Ior Maven / Gradle / Ivy

There is a newer version: 10.4.1
Show newest version
package cyclops.control;

import com.oath.cyclops.hkt.Higher;
import com.oath.cyclops.hkt.Higher2;
import com.oath.cyclops.types.Filters;
import com.oath.cyclops.types.OrElseValue;
import com.oath.cyclops.types.Value;
import com.oath.cyclops.types.factory.Unit;
import com.oath.cyclops.types.foldable.To;
import com.oath.cyclops.types.functor.BiTransformable;
import com.oath.cyclops.types.functor.Transformable;
import com.oath.cyclops.types.reactive.ValueSubscriber;
import cyclops.function.*;
import com.oath.cyclops.hkt.DataWitness.ior;
import cyclops.reactive.ReactiveSeq;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import cyclops.data.tuple.Tuple;
import cyclops.data.tuple.Tuple2;
import org.reactivestreams.Publisher;

import java.util.Iterator;
import java.util.Optional;
import java.util.function.*;

/**
 * Inclusive Or (can be one of Primary, Secondary or Both Primary and Secondary)
 *
 * An Either or Union type, but right biased. Primary and Secondary are used instead of Right & Left.
 * 'Right' (or right type) biased disjunct union.
 *  No 'projections' are provided, swap() and secondaryXXXX alternative methods can be used instead.
 *
 *
 *  For exclusive or @see Either
 *
 * @author johnmcclean
 *
 * @param  Left type
 * @param  Right type
 */
public interface Ior extends To>, Value,OrElseValue>,Unit, Transformable, Filters,  BiTransformable ,Higher2 {

    public static  Higher, T> widen(Ior narrow) {
    return narrow;
  }


    Ior recover(Supplier value);
    Ior recover(RT value);
    Ior recoverWith(Supplier> fn);

    default int arity(){
        return 2;
    }
    /**
     * Static method useful as a method reference for fluent consumption of any value type stored in this Either
     * (will capture the lowest common type)
     *
     * 
     * {@code
     *
     *   myEither.to(Xor::consumeAny)
    .accept(System.out::println);
     * }
     * 
* * @param either Xor to consume value for * @return Consumer we can applyHKT to consume value */ static Consumer> consumeAny(Ior either){ return in->visitAny(in,either); } static Function,R> applyAny(Ior either){ return in->visitAny(either,in); } @Deprecated //use foldAny static R visitAny(Ior either, Function fn){ return foldAny(either,fn); } static R foldAny(Ior either, Function fn){ return either.fold(fn, fn, (a, b)-> fn.apply(a)); } static X visitAny(Consumer c,Ior either){ Function fn = x ->{ c.accept(x); return x; }; return visitAny(either,fn); } /** * Construct an Ior that contains a single value extracted from the supplied reactive-streams Publisher *
     * {@code
     *   ReactiveSeq stream =  ReactiveSeq.of(1,2,3);

        Ior future = Ior.fromPublisher(stream);

        //Ior[1]
     *
     * }
     * 
* * @param pub Publisher to extract value from * @return Ior populated from Publisher */ public static Ior fromPublisher(final Publisher pub) { final ValueSubscriber sub = ValueSubscriber.subscriber(); pub.subscribe(sub); return sub.toEither() .toIor(); } /** * Construct an Ior that contains a single value extracted from the supplied Iterable *
     * {@code
     *   List list =  Arrays.asList(1,2,3);

        Ior future = Ior.fromPublisher(list);

        //Ior[1]
     *
     * }
     * 
* @param iterable Iterable to extract value from * @return Ior populated from Iterable */ public static Ior fromIterable(final Iterable iterable, T alt) { final Iterator it = iterable.iterator(); return Ior.right(it.hasNext() ? it.next() : alt); } /** * Create an instance of the right type. Most methods are biased to the right type, * which means, for example, that the transform method operates on the right type but does nothing on left Iors * *
     * {@code
     *   Ior.right(10).map(i->i+1);
     * //Ior.right[11]
     *
     *
     * }
     * 
* * * @param right To construct an Ior from * @return Primary type instanceof Ior */ public static Ior right(final RT right) { return new Primary<>( right); } /** * Create an instance of the left type. Most methods are biased to the right type, * so you will need to use swap() or leftXXXX to manipulate the wrapped value * *
     * {@code
     *   Ior.left(10).map(i->i+1);
     *   //Ior.left[10]
     *
     *    Ior.left(10).swap().map(i->i+1);
     *    //Ior.right[11]
     * }
     * 
* * * @param left to wrap * @return Secondary instance of Ior */ public static Ior left(final LT left) { return new Secondary<>( left); } /** * Create an Ior instance that contains both left and right types * *
     * {@code
     *    Ior kv = Ior.both("hello",90);
     *    //Ior["hello",90]
     * }
     * 
* * @param left Secondary value * @param right Primary value * @return Ior that contains both the left and the right value */ public static Ior both(final ST left, final PT right) { return new Both( left, right); } default Ior forEach4(Function> value1, BiFunction> value2, Function3> value3, Function4 yieldingFunction) { return this.flatMap(in-> { Ior a = value1.apply(in); return a.flatMap(ina-> { Ior b = value2.apply(in,ina); return b.flatMap(inb-> { Ior c= value3.apply(in,ina,inb); return c.map(in2->yieldingFunction.apply(in,ina,inb,in2)); }); }); }); } default Ior forEach3(Function> value1, BiFunction> value2, Function3 yieldingFunction) { return this.flatMap(in-> { Ior a = value1.apply(in); return a.flatMap(ina-> { Ior b = value2.apply(in,ina); return b.map(in2->yieldingFunction.apply(in,ina, in2)); }); }); } default Ior forEach2(Function> value1, BiFunction yieldingFunction) { return this.flatMap(in-> { Ior b = value1.apply(in); return b.map(in2->yieldingFunction.apply(in, in2)); }); } /* (non-Javadoc) * @see com.oath.cyclops.types.MonadicValue#unit(java.lang.Object) */ @Override default Ior unit(final T unit) { return Ior.right(unit); } /* (non-Javadoc) * @see com.oath.cyclops.types.Filters#filter(java.util.function.Predicate) */ @Override Option filter(Predicate test); /* (non-Javadoc) * @see com.oath.cyclops.types.Value#toLazyEither() */ Either toEither(); /** * @return Convert to an Either, dropping the right type if this Ior contains both */ Either toEitherDropRight(); //drop PT /* (non-Javadoc) * @see com.oath.cyclops.types.Value#toLazyEither(java.lang.Object) */ @Override default Either toEither(final ST2 secondary) { return fold(s -> Either.left(secondary), p -> Either.right(p), (s, p) -> Either.right(p)); } Ior mapLeft(Function fn); @Override Ior map(Function fn); Ior peekLeft(Consumer action); /* (non-Javadoc) * @see com.oath.cyclops.types.functor.Transformable#peek(java.util.function.Consumer) */ @Override Ior peek(Consumer action); /** * @return Ior with Primary and Secondary types and value swapped */ Ior swap(); default Ior coflatMap(final Function, R> mapper) { return mapper.andThen(r -> unit(r)) .apply(this); } //cojoin default Ior> nest() { return this.map(t -> unit(t)); } /** * @return An zero Option if this Ior only has lazy the Secondary or Primary type. Or an Optional containing a Tuple2 * with both the Secondary and Primary types if they are both present. */ Option> both(); /* (non-Javadoc) * @see com.oath.cyclops.types.functor.BiTransformable#bimap(java.util.function.Function, java.util.function.Function) */ @Override Ior bimap(final Function fn1, final Function fn2); /** * Visitor pattern for this Ior. * Execute the left function if this Ior contains an element of the left type only * Execute the right function if this Ior contains an element of the right type only * Execute the both function if this Ior contains an element of both type * *
     * {@code
     *  Ior.right(10)
     *     .visit(left->"no", right->"yes",(sec,pri)->"oops!")
     *  //Ior["yes"]

        Ior.left(90)
           .visit(left->"no", right->"yes",(sec,pri)->"oops!")
        //Ior["no"]

        Ior.both(10, "eek")
           .visit(left->"no", right->"yes",(sec,pri)->"oops!")
        //Ior["oops!"]
     *
     *
     * }
     * 
* * @param secondary Function to execute if this is a Secondary Ior * @param primary Function to execute if this is a Primary Ior * @param both Function to execute if this Ior contains both types * @return Result of executing the appropriate function */ R fold(final Function secondary, final Function primary, final BiFunction both) ; /* (non-Javadoc) * @see java.util.function.Supplier#getValue() */ Option get(); /* (non-Javadoc) * @see com.oath.cyclops.types.foldable.Convertable#isPresent() */ @Override default boolean isPresent() { return isRight() || isBoth(); } Option getLeft(); public Ior flatMap(final Function> mapper); /** * Perform a flatMap operation on the Secondary type * * @param mapper Flattening transformation function * @return Ior containing the value inside the result of the transformation function as the Secondary value, if the Secondary type was present */ Ior flatMapLeft(Function> mapper); /** * @return True if this is a right (only) Ior */ public boolean isRight(); /** * @return True if this was a left (only) Ior */ public boolean isLeft(); /** * @return True if this Ior has both left and right types */ public boolean isBoth(); /** * Turn a toX of Iors into a single Ior with Lists of values. * Primary and left types are swapped during this operation. * *
     * {@code
     *  Ior just  = Ior.right(10);
        Ior none = Ior.left("none");
     *  Ior,Seq> iors =Ior.sequenceLeft(Seq.of(just,none,Ior.right(1)));
        //Ior.right(Seq.of("none")))
     *
     * }
     * 
* * * @param iors Iors to sequence * @return Ior sequenced and swapped */ public static Ior> sequenceLeft(final Iterable> iors) { return sequence(ReactiveSeq.fromIterable(iors).filterNot(Ior::isRight).map(Ior::swap)); } /** * Accumulate the result of the Secondary types in the Collection of Iors provided using the supplied Reducer {@see cyclops2.Reducers}. * *
     * {@code
     *  Ior just  = Ior.right(10);
        Ior none = Ior.left("none");
     *  Ior> iors = Ior.accumulateLeft(Seq.of(just,none,Ior.right(1)),Reducers.toPersistentSetX());
      //Ior.right(PersistentSetX.of("none"))));
      * }
     * 
* @param iors Collection of Iors to accumulate left values * @param reducer Reducer to accumulate results * @return Ior populated with the accumulate left operation */ public static Ior accumulateLeft(final Iterable> iors, final Reducer reducer) { return sequenceLeft(iors).map(s -> s.foldMap(reducer)); } /** * Accumulate the results only from those Iors which have a Secondary type present, using the supplied mapping function to * convert the data from each Ior before reducing them using the supplied Monoid (a combining BiFunction/BinaryOperator and identity element that takes two * input values of the same type and returns the combined result) {@see cyclops2.Monoids }. * *
     * {@code
     *  Ior just  = Ior.right(10);
        Ior none = Ior.left("none");

     *  Ior iors = Ior.accumulateLeft(Seq.of(just,none,Ior.left("1")),i->""+i,Monoids.stringConcat);
        //Ior.right("none1")
     *
     * }
     * 
* * * * @param iors Collection of Iors to accumulate left values * @param mapper Mapping function to be applied to the result of each Ior * @param reducer Semigroup to combine values from each Ior * @return Ior populated with the accumulate Secondary operation */ public static Ior accumulateLeft(final Iterable> iors, final Function mapper, final Monoid reducer) { return sequenceLeft(iors).map(s -> s.map(mapper) .reduce(reducer)); } /** * Accumulate the results only from those Iors which have a Secondary type present, using the supplied Monoid (a combining BiFunction/BinaryOperator and identity element that takes two * input values of the same type and returns the combined result) {@see cyclops2.Monoids }. * *
     * {@code
     *
     *  Ior just  = Ior.right(10);
        Ior none = Ior.left("none");

     * Ior iors = Ior.accumulateLeft(Monoids.intSum,Seq.of(Ior.both(2, "boo!"),Ior.left(1)));
       //Ior.right(3);  2+1
     *
     *
     * }
     * 
* * * @param iors Collection of Iors to accumulate left values * @param reducer Semigroup to combine values from each Ior * @return populated with the accumulate Secondary operation */ public static Ior accumulateLeft(final Monoid reducer, final Iterable> iors) { return sequenceLeft(iors).map(s -> s.reduce(reducer)); } /** * Turn a toX of Iors into a single Ior with Lists of values. * *
     * {@code
     *
     * Ior just  = Ior.right(10);
       Ior none = Ior.left("none");


     * Ior> iors =Ior.sequenceRight(Seq.of(just,none,Ior.right(1)));
       //Ior.right(Seq.of(10,1)));
     *
     * }
* * * * @param iors Iors to sequence * @return Ior Sequenced */ public static Ior> sequenceRight(final Iterable> iors) { return sequence(ReactiveSeq.fromIterable(iors).filterNot(Ior::isLeft)); } public static Ior> sequence(ReactiveSeq> stream) { Ior> identity = right(ReactiveSeq.empty()); BiFunction>,Ior,Ior>> combineToStream = (acc,next) ->acc.zip(next,(a,b)->a.append(b)); BinaryOperator>> combineStreams = (a,b)-> a.zip(b,(z1,z2)->z1.appendStream(z2)); return stream.reduce(identity,combineToStream,combineStreams); } public static Ior> traverse(Function fn,ReactiveSeq> stream) { return sequence(stream.map(h->h.map(fn))); } /** * Accumulate the result of the Primary types in the Collection of Iors provided using the supplied Reducer {@see cyclops2.Reducers}. *
     * {@code
     *  Ior just  = Ior.right(10);
        Ior none = Ior.left("none");
     *
     *  Ior> iors =Ior.accumulateRight(Seq.of(just,none,Ior.right(1)),Reducers.toPersistentSetX());
        //Ior.right(PersistentSetX.of(10,1))));
     * }
     * 
* @param iors Collection of Iors to accumulate right values * @param reducer Reducer to accumulate results * @return Ior populated with the accumulate right operation */ public static Ior accumulateRight(final Iterable> iors, final Reducer reducer) { return sequenceRight(iors).map(s -> s.foldMap(reducer)); } /** * Accumulate the results only from those Iors which have a Primary type present, using the supplied mapping function to * convert the data from each Ior before reducing them using the supplied Semgigroup (a combining BiFunction/BinaryOperator that takes two * input values of the same type and returns the combined result) {@see cyclops2.SemigroupK }. * *
     * {@code
     *  Ior just  = Ior.right(10);
        Ior none = Ior.left("none");

     * Ior iors = Ior.accumulateRight(Seq.of(just,none,Ior.right(1)),i->""+i,SemigroupK.stringConcat);
       //Ior.right("101"));
     * }
     * 
* * * @param iors Collection of Iors to accumulate right values * @param mapper Mapping function to be applied to the result of each Ior * @param reducer Reducer to accumulate results * @return Ior populated with the accumulate right operation */ public static Ior accumulateRight(final Iterable> iors, final Function mapper, final Semigroup reducer) { return sequenceRight(iors).map(s -> s.map(mapper) .reduce(reducer) .get()); } /** * Accumulate the results only from those Iors which have a Primary type present, using the supplied Semgigroup (a combining BiFunction/BinaryOperator that takes two * input values of the same type and returns the combined result) {@see cyclops2.SemigroupK }. * *
     * {@code
     *  Ior just  = Ior.right(10);
        Ior none = Ior.left("none");
     *
     *  Ior iors =Ior.accumulateRight(Seq.of(just,none,Ior.right(1)),SemigroupK.intSum);
        //Ior.right(11);
     *
     * }
     * 
* * * * @param iors Collection of Iors to accumulate right values * @param reducer Reducer to accumulate results * @return Ior populated with the accumulate right operation */ public static Ior accumulateRight(final Iterable> iors, final Semigroup reducer) { return sequenceRight(iors).map(s -> s.reduce(reducer) .get()); } /* (non-Javadoc) * @see com.oath.cyclops.lambda.monads.Filters#ofType(java.lang.Class) */ @Override default Option ofType(final Class type) { return (Option) Filters.super.ofType(type); } /* (non-Javadoc) * @see com.oath.cyclops.lambda.monads.Filters#filterNot(java.util.function.Predicate) */ @Override default Option filterNot(final Predicate fn) { return (Option) Filters.super.filterNot(fn); } @Override default Option notNull() { return (Option) Filters.super.notNull(); } /* (non-Javadoc) * @see com.oath.cyclops.lambda.monads.BiTransformable#bipeek(java.util.function.Consumer, java.util.function.Consumer) */ @Override default Ior bipeek(final Consumer c1, final Consumer c2) { return (Ior) BiTransformable.super.bipeek(c1, c2); } default Ior zip(final Ior app, final BiFunction fn){ return flatMap(t->app.map(t2->fn.apply(t,t2))); } default Ior zip(final Either app, final BiFunction fn){ return flatMap(t->app.map(t2->fn.apply(t,t2)).toIor()); } @AllArgsConstructor(access = AccessLevel.PRIVATE) @EqualsAndHashCode(of = { "value" }) public static class Primary implements Ior { private final PT value; @Override public Either toEither() { return Either.right(value); } @Override public Either toEitherDropRight() { return Either.right(value); } @Override public Ior recover(Supplier value) { return this; } @Override public Ior recover(PT value) { return this; } @Override public Ior recoverWith(Supplier> fn) { return this; } @Override public Ior mapLeft(final Function fn) { return (Ior) this; } @Override public Ior map(final Function fn) { return new Primary( fn.apply(value)); } @Override public Ior peekLeft(final Consumer action) { return this; } @Override public R fold(final Function secondary, final Function primary, final BiFunction both) { return primary.apply(value); } @Override public Ior peek(final Consumer action) { action.accept(value); return this; } @Override public Option filter(final Predicate test) { if (test.test(value)) return Option.some(value); return Option.none(); } @Override public Ior bimap(final Function fn1, final Function fn2) { return Ior.right(fn2.apply(value)); } @Override public Ior swap() { return new Secondary( value); } @Override public Option get() { return Option.some(value); } @Override public Option getLeft() { return Option.none(); } @Override public Ior flatMap(final Function> mapper) { Ior x = mapper.apply(value); return (Ior)x; } @Override public Ior flatMapLeft(final Function> mapper) { return (Ior) this; } @Override public Ior bipeek(final Consumer stAction, final Consumer ptAction) { ptAction.accept(value); return this; } @Override public Option> both() { return Option.none(); } @Override public boolean isRight() { return true; } @Override public boolean isLeft() { return false; } @Override public boolean isBoth() { return false; } @Override public String toString() { return mkString(); } @Override public String mkString() { return "Ior.right[" + value + "]"; } @Override public R fold(Function present, Supplier absent) { return present.apply(value); } } @AllArgsConstructor(access = AccessLevel.PRIVATE) @EqualsAndHashCode(of = {"value"}) public static class Secondary implements Ior { private final ST value; @Override public Ior recover(Supplier value) { return Ior.both(this.value,value.get()); } @Override public Ior recover(PT value) { return Ior.both(this.value,value); } @Override public Ior recoverWith(Supplier> fn) { return fn.get(); } @Override public boolean isLeft() { return true; } @Override public boolean isRight() { return false; } @Override public Either toEither() { return Either.left(value); } @Override public Either toEitherDropRight() { return Either.left(value); } @Override public Ior mapLeft(final Function fn) { return new Secondary( fn.apply(value)); } @Override public Ior map(final Function fn) { return (Ior) this; } @Override public Ior peekLeft(final Consumer action) { return mapLeft((Function) FluentFunctions.expression(action)); } @Override public Option> both() { return Option.none(); } @Override public Ior peek(final Consumer action) { return this; } @Override public Option filter(final Predicate test) { return Option.none(); } @Override public Ior bimap(final Function fn1, final Function fn2) { return Ior.left(fn1.apply(value)); } @Override public Ior swap() { return new Primary( value); } @Override public Option get() { return Option.none(); } @Override public Option getLeft() { return Option.some(value); } @Override public Ior flatMap(final Function> mapper) { return (Ior) this; } @Override public Ior flatMapLeft(final Function> mapper) { return mapper.apply(value); } @Override public Ior bipeek(final Consumer stAction, final Consumer ptAction) { stAction.accept(value); return this; } @Override public R fold(final Function secondary, final Function primary, final BiFunction both) { return swap().fold(secondary, () -> null); } @Override public boolean isBoth() { return false; } @Override public Maybe toMaybe() { return Maybe.nothing(); } @Override public Optional toOptional() { return Optional.empty(); } @Override public String toString() { return mkString(); } @Override public String mkString() { return "Ior.left[" + value + "]"; } @Override public R fold(Function present, Supplier absent) { return absent.get(); } } @AllArgsConstructor(access = AccessLevel.PACKAGE) @EqualsAndHashCode(of = {"left", "right"}) public static class Both implements Ior { private final ST secondary; private final PT primary; private static Ior both(final ST secondary, final PT primary) { return new Both( secondary, primary); } @Override public ReactiveSeq stream() { return ReactiveSeq.of(primary); } @Override public Ior recover(Supplier value) { return this; } @Override public Ior recover(PT value) { return this; } @Override public Ior recoverWith(Supplier> fn) { return this; } @Override public Iterator iterator() { return stream().iterator(); } @Override public Either toEither() { return Either.right(primary); } @Override public Either toEitherDropRight() { return Either.left(secondary); } @Override public Ior mapLeft(final Function fn) { return Both.both(fn.apply(secondary), primary); } @Override public Ior map(final Function fn) { return Both.both(secondary, fn.apply(primary)); } @Override public Ior peekLeft(final Consumer action) { action.accept(secondary); return this; } @Override public Ior peek(final Consumer action) { action.accept(primary); return this; } @Override public Option filter(final Predicate test) { return Either.right(primary).filter(test); } @Override public Ior swap() { return Both.both(primary, secondary); } @Override public R fold(final Function secondary, final Function primary, final BiFunction both) { return both.apply(this.secondary,this.primary); } @Override public Option> both() { return Option.some(Tuple.tuple(secondary, primary)); } @Override public Option get() { return Option.of(primary); } @Override public Option getLeft() { return Option.of(secondary); } @Override public Ior flatMap(final Function> mapper) { Ior x = mapper.apply(primary); return (Ior)x; } @Override public Ior flatMapLeft(final Function> mapper) { return mapper.apply(secondary); } @Override public Ior bipeek(final Consumer actionA, final Consumer actionB) { actionA.accept(secondary); actionB.accept(primary); return this; } @Override public Ior bimap(final Function fn1, final Function fn2) { return Both.both(fn1.apply(secondary), fn2.apply(primary)); } @Override public boolean isRight() { return false; } @Override public boolean isLeft() { return false; } @Override public boolean isBoth() { return true; } @Override public String toString() { return mkString(); } @Override public String mkString() { return "Ior.both[" + primary.toString() + ":" + secondary.toString() + "]"; } @Override public R fold(Function present, Supplier absent) { return present.apply(primary); } } public static Ior narrowK2(final Higher2 ior) { return (Ior)ior; } public static Ior narrowK(final Higher,T> ior) { return (Ior)ior; } }