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

cyclops.companion.vavr.Options Maven / Gradle / Ivy

package cyclops.companion.vavr;

import cyclops.monads.VavrWitness.tryType;
import io.vavr.Lazy;
import io.vavr.collection.*;
import io.vavr.control.*;
import com.aol.cyclops.vavr.hkt.*;
import cyclops.companion.CompletableFutures;
import cyclops.companion.Optionals;
import cyclops.control.Eval;
import cyclops.control.Maybe;
import cyclops.control.Reader;
import cyclops.control.Xor;
import cyclops.conversion.vavr.FromCyclopsReact;
import cyclops.monads.*;
import cyclops.monads.VavrWitness.*;
import com.aol.cyclops2.hkt.Higher;
import cyclops.function.Fn3;
import cyclops.function.Fn4;
import cyclops.function.Monoid;
import cyclops.monads.Witness.*;
import cyclops.stream.ReactiveSeq;
import cyclops.typeclasses.*;
import com.aol.cyclops.vavr.hkt.FutureKind;
import cyclops.conversion.vavr.ToCyclopsReact;
import cyclops.monads.VavrWitness;
import cyclops.monads.VavrWitness.option;
import com.aol.cyclops.vavr.hkt.OptionKind;
import com.aol.cyclops2.data.collections.extensions.CollectionX;
import com.aol.cyclops2.types.Value;
import com.aol.cyclops2.types.anyM.AnyMValue;
import cyclops.collections.mutable.ListX;
import cyclops.companion.Monoids;
import cyclops.function.Reducer;
import cyclops.monads.AnyM;
import cyclops.monads.WitnessType;
import cyclops.monads.XorM;
import cyclops.monads.transformers.OptionalT;
import cyclops.typeclasses.comonad.Comonad;
import cyclops.typeclasses.foldable.Foldable;
import cyclops.typeclasses.foldable.Unfoldable;
import cyclops.typeclasses.functor.Functor;
import cyclops.typeclasses.instances.General;
import cyclops.typeclasses.monad.*;
import io.vavr.concurrent.Future;
import io.vavr.control.Option;
import lombok.experimental.UtilityClass;
import org.reactivestreams.Publisher;

import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;


import static com.aol.cyclops.vavr.hkt.OptionKind.widen;

/**
 * Utility class for working with JDK Optionals
 *
 * @author johnmcclean
 *
 */
@UtilityClass
public class Options {
    public static   Coproduct coproduct(Option type, InstanceDefinitions def1){
        return Coproduct.of(Xor.primary(widen(type)),def1, Instances.definitions());
    }
    public static   Coproduct coproduct(T value, InstanceDefinitions def1){
        return coproduct(Option.some(value),def1);
    }
    public static   Coproduct coproductNone(InstanceDefinitions def1){
        return coproduct(Option.none(),def1);
    }
    public static  ,T> XorM xorM(Option type){
        return XorM.right(anyM(type));
    }
    public static  ,T> XorM xorM(T type){
        return XorM.right(anyM(Option.some(type)));
    }
    public static  ,T> XorM xorMNone(){
        return XorM.right(anyM(Option.none()));
    }
    public static > OptionalT liftM(Option opt, W witness) {
        return OptionalT.of(witness.adapter().unit(opt.toJavaOptional()));
    }
    public static  AnyMValue anyM(Option option) {
        return AnyM.ofValue(option, VavrWitness.option.INSTANCE);
    }
    /**
     * Perform a For Comprehension over a Option, accepting 3 generating function.
     * This results in a four level nested internal iteration over the provided Options.
     *
     *  
     * {@code
     *
     *   import static com.aol.cyclops2.reactor.Options.forEach4;
     *
    forEach4(Option.just(1),
    a-> Option.just(a+1),
    (a,b) -> Option.just(a+b),
    a                  (a,b,c) -> Option.just(a+b+c),
    Tuple::tuple)
     *
     * }
     * 
* * @param value1 top level Option * @param value2 Nested Option * @param value3 Nested Option * @param value4 Nested Option * @param yieldingFunction Generates a result per combination * @return Option with a combined value generated by the yielding function */ public static Option forEach4(Option value1, Function> value2, BiFunction> value3, Fn3> value4, Fn4 yieldingFunction) { return value1.flatMap(in -> { Option a = value2.apply(in); return a.flatMap(ina -> { Option b = value3.apply(in,ina); return b.flatMap(inb -> { Option c = value4.apply(in,ina,inb); return c.map(in2 -> yieldingFunction.apply(in, ina, inb, in2)); }); }); }); } /** * * Perform a For Comprehension over a Option, accepting 3 generating function. * This results in a four level nested internal iteration over the provided Options. * *
     * {@code
     *
     *  import static com.aol.cyclops2.reactor.Options.forEach4;
     *
     *  forEach4(Option.just(1),
    a-> Option.just(a+1),
    (a,b) -> Option.just(a+b),
    (a,b,c) -> Option.just(a+b+c),
    (a,b,c,d) -> a+b+c+d <100,
    Tuple::tuple);
     *
     * }
     * 
* * @param value1 top level Option * @param value2 Nested Option * @param value3 Nested Option * @param value4 Nested Option * @param filterFunction A filtering function, keeps values where the predicate holds * @param yieldingFunction Generates a result per combination * @return Option with a combined value generated by the yielding function */ public static Option forEach4(Option value1, Function> value2, BiFunction> value3, Fn3> value4, Fn4 filterFunction, Fn4 yieldingFunction) { return value1.flatMap(in -> { Option a = value2.apply(in); return a.flatMap(ina -> { Option b = value3.apply(in,ina); return b.flatMap(inb -> { Option c = value4.apply(in,ina,inb); return c.filter(in2->filterFunction.apply(in,ina,inb,in2)) .map(in2 -> yieldingFunction.apply(in, ina, inb, in2)); }); }); }); } /** * Perform a For Comprehension over a Option, accepting 2 generating function. * This results in a three level nested internal iteration over the provided Options. * *
     * {@code
     *
     *   import static com.aol.cyclops2.reactor.Options.forEach3;
     *
    forEach3(Option.just(1),
    a-> Option.just(a+1),
    (a,b) -> Option.just(a+b),
    Tuple::tuple)
     *
     * }
     * 
* * @param value1 top level Option * @param value2 Nested Option * @param value3 Nested Option * @param yieldingFunction Generates a result per combination * @return Option with a combined value generated by the yielding function */ public static Option forEach3(Option value1, Function> value2, BiFunction> value3, Fn3 yieldingFunction) { return value1.flatMap(in -> { Option a = value2.apply(in); return a.flatMap(ina -> { Option b = value3.apply(in,ina); return b.map(in2 -> yieldingFunction.apply(in, ina, in2)); }); }); } /** * * Perform a For Comprehension over a Option, accepting 2 generating function. * This results in a three level nested internal iteration over the provided Options. * *
     * {@code
     *
     *  import static com.aol.cyclops2.reactor.Options.forEach3;
     *
     *  forEach3(Option.just(1),
    a-> Option.just(a+1),
    (a,b) -> Option.just(a+b),
    (a,b,c) -> a+b+c <100,
    Tuple::tuple);
     *
     * }
     * 
* * @param value1 top level Option * @param value2 Nested Option * @param value3 Nested Option * @param filterFunction A filtering function, keeps values where the predicate holds * @param yieldingFunction Generates a result per combination * @return Option with a combined value generated by the yielding function */ public static Option forEach3(Option value1, Function> value2, BiFunction> value3, Fn3 filterFunction, Fn3 yieldingFunction) { return value1.flatMap(in -> { Option a = value2.apply(in); return a.flatMap(ina -> { Option b = value3.apply(in,ina); return b.filter(in2->filterFunction.apply(in,ina,in2)) .map(in2 -> yieldingFunction.apply(in, ina, in2)); }); }); } /** * Perform a For Comprehension over a Option, accepting a generating function. * This results in a two level nested internal iteration over the provided Options. * *
     * {@code
     *
     *   import static com.aol.cyclops2.reactor.Options.forEach;
     *
    forEach(Option.just(1),
    a-> Option.just(a+1),
    Tuple::tuple)
     *
     * }
     * 
* * @param value1 top level Option * @param value2 Nested Option * @param yieldingFunction Generates a result per combination * @return Option with a combined value generated by the yielding function */ public static Option forEach2(Option value1, Function> value2, BiFunction yieldingFunction) { return value1.flatMap(in -> { Option a = value2.apply(in); return a.map(in2 -> yieldingFunction.apply(in, in2)); }); } /** * * Perform a For Comprehension over a Option, accepting a generating function. * This results in a two level nested internal iteration over the provided Options. * *
     * {@code
     *
     *  import static com.aol.cyclops2.reactor.Options.forEach;
     *
     *  forEach(Option.just(1),
    a-> Option.just(a+1),
    (a,b) -> Option.just(a+b),
    (a,b,c) -> a+b+c <100,
    Tuple::tuple);
     *
     * }
     * 
* * @param value1 top level Option * @param value2 Nested Option * @param filterFunction A filtering function, keeps values where the predicate holds * @param yieldingFunction Generates a result per combination * @return Option with a combined value generated by the yielding function */ public static Option forEach2(Option value1, Function> value2, BiFunction filterFunction, BiFunction yieldingFunction) { return value1.flatMap(in -> { Option a = value2.apply(in); return a.filter(in2->filterFunction.apply(in,in2)) .map(in2 -> yieldingFunction.apply(in, in2)); }); } public static Option optional(OptionalDouble d){ return d.isPresent() ? Option.of(d.getAsDouble()) : Option.none(); } public static Option optional(OptionalLong l){ return l.isPresent() ? Option.of(l.getAsLong()) : Option.none(); } public static Option optional(OptionalInt l){ return l.isPresent() ? Option.of(l.getAsInt()) : Option.none(); } /** * Sequence operation, take a Collection of Options and turn it into a Option with a Collection * By constrast with {@link Options#sequencePresent(CollectionX)}, if any Options are empty the result * is an empty Option * *
     * {@code
     *
     *  Option just = Option.of(10);
    Option none = Option.empty();
     *
     *  Option> opts = Options.sequence(ListX.of(just, none, Option.of(1)));
    //Option.empty();
     *
     * }
     * 
* * * @param opts Maybes to Sequence * @return Maybe with a List of values */ public static Option> sequence(final CollectionX> opts) { return sequence(opts.stream()).map(s -> s.toListX()); } /** * Sequence operation, take a Collection of Options and turn it into a Option with a Collection * Only successes are retained. By constrast with {@link Options#sequence(CollectionX)} Option#empty types are * tolerated and ignored. * *
     * {@code
     *  Option just = Option.of(10);
    Option none = Option.empty();
     *
     * Option> maybes = Options.sequencePresent(ListX.of(just, none, Option.of(1)));
    //Option.of(ListX.of(10, 1));
     * }
     * 
* * @param opts Options to Sequence * @return Option with a List of values */ public static Option> sequencePresent(final CollectionX> opts) { return sequence(opts.stream().filter(Option::isDefined)).map(s->s.toListX()); } /** * Sequence operation, take a Collection of Options and turn it into a Option with a Collection * By constrast with {@link Options#sequencePresent(CollectionX)} if any Option types are empty * the return type will be an empty Option * *
     * {@code
     *
     *  Option just = Option.of(10);
    Option none = Option.empty();
     *
     *  Option> maybes = Options.sequence(ListX.of(just, none, Option.of(1)));
    //Option.empty();
     *
     * }
     * 
* * * @param opts Maybes to Sequence * @return Option with a List of values */ public static Option> sequence(final java.util.stream.Stream> opts) { return AnyM.sequence(opts.map(Options::anyM), option.INSTANCE) .map(ReactiveSeq::fromStream) .to(VavrWitness::option); } /** * Accummulating operation using the supplied Reducer (@see cyclops2.Reducers). A typical use case is to accumulate into a Persistent Collection type. * Accumulates the present results, ignores empty Options. * *
     * {@code
     *  Option just = Option.of(10);
    Option none = Option.empty();

     * Option> opts = Option.accumulateJust(ListX.of(just, none, Option.of(1)), Reducers.toPersistentSetX());
    //Option.of(PersistentSetX.of(10, 1)));
     *
     * }
     * 
* * @param optionals Options to accumulate * @param reducer Reducer to accumulate values with * @return Option with reduced value */ public static Option accumulatePresent(final CollectionX> optionals, final Reducer reducer) { return sequencePresent(optionals).map(s -> s.mapReduce(reducer)); } /** * Accumulate the results only from those Options which have a value present, using the supplied mapping function to * convert the data from each Option 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
     *  Option just = Option.of(10);
    Option none = Option.empty();

     *  Option opts = Option.accumulateJust(ListX.of(just, none, Option.of(1)), i -> "" + i,
    Monoids.stringConcat);
    //Option.of("101")
     *
     * }
     * 
* * @param optionals Options to accumulate * @param mapper Mapping function to be applied to the result of each Option * @param reducer Monoid to combine values from each Option * @return Option with reduced value */ public static Option accumulatePresent(final CollectionX> optionals, final Function mapper, final Monoid reducer) { return sequencePresent(optionals).map(s -> s.map(mapper) .reduce(reducer)); } /** * Accumulate the results only from those Options which have a value 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
     *  Option just = Option.of(10);
    Option none = Option.empty();

     *  Option opts = Option.accumulateJust(Monoids.stringConcat,ListX.of(just, none, Option.of(1)),
    );
    //Option.of("101")
     *
     * }
     * 
* * @param optionals Options to accumulate * @param reducer Monoid to combine values from each Option * @return Option with reduced value */ public static Option accumulatePresent(final Monoid reducer, final CollectionX> optionals) { return sequencePresent(optionals).map(s -> s .reduce(reducer)); } /** * Combine an Option with the provided value using the supplied BiFunction * *
     * {@code
     *  Options.combine(Option.of(10),Maybe.just(20), this::add)
     *  //Option[30]
     *
     *  private int add(int a, int b) {
    return a + b;
    }
     *
     * }
     * 
* @param f Option to combine with a value * @param v Value to combine * @param fn Combining function * @return Option combined with supplied value */ public static Option combine(final Option f, final Value v, final BiFunction fn) { return narrow(FromCyclopsReact.option(ToCyclopsReact.maybe(f) .combine(v, fn))); } /** * Combine an Option with the provided Option using the supplied BiFunction * *
     * {@code
     *  Options.combine(Option.of(10),Option.of(20), this::add)
     *  //Option[30]
     *
     *  private int add(int a, int b) {
    return a + b;
    }
     *
     * }
     * 
* * @param f Option to combine with a value * @param v Option to combine * @param fn Combining function * @return Option combined with supplied value, or empty Option if no value present */ public static Option combine(final Option f, final Option v, final BiFunction fn) { return combine(f,ToCyclopsReact.maybe(v),fn); } /** * Combine an Option with the provided Iterable (selecting one element if present) using the supplied BiFunction *
     * {@code
     *  Options.zip(Option.of(10),Arrays.asList(20), this::add)
     *  //Option[30]
     *
     *  private int add(int a, int b) {
    return a + b;
    }
     *
     * }
     * 
* @param f Option to combine with first element in Iterable (if present) * @param v Iterable to combine * @param fn Combining function * @return Option combined with supplied Iterable, or empty Option if no value present */ public static Option zip(final Option f, final Iterable v, final BiFunction fn) { return narrow(FromCyclopsReact.option(ToCyclopsReact.maybe(f) .zip(v, fn))); } /** * Combine an Option with the provided Publisher (selecting one element if present) using the supplied BiFunction *
     * {@code
     *  Options.zip(Flux.just(10),Option.of(10), this::add)
     *  //Option[30]
     *
     *  private int add(int a, int b) {
    return a + b;
    }
     *
     * }
     * 
* * @param p Publisher to combine * @param f Option to combine with * @param fn Combining function * @return Option combined with supplied Publisher, or empty Option if no value present */ public static Option zip(final Publisher p, final Option f, final BiFunction fn) { return narrow(FromCyclopsReact.option(ToCyclopsReact.maybe(f) .zipP(p, fn))); } /** * Narrow covariant type parameter * * @param optional Option with covariant type parameter * @return Narrowed Option */ public static Option narrow(final Option optional) { return (Option) optional; } public static Active allTypeclasses(Option option){ return Active.of(widen(option), Options.Instances.definitions()); } public static Nested mapM(Option option, Function> fn, InstanceDefinitions defs){ Option> e = option.map(fn); OptionKind> lk = widen(e); return Nested.of(lk, Options.Instances.definitions(), defs); } /** * Companion class for creating Type Class instances for working with Options * @author johnmcclean * */ @UtilityClass public static class Instances { public static InstanceDefinitions